diff options
Diffstat (limited to 'tor_extension.php')
| -rw-r--r-- | tor_extension.php | 600 |
1 files changed, 363 insertions, 237 deletions
diff --git a/tor_extension.php b/tor_extension.php index e4ab9b5..9611cad 100644 --- a/tor_extension.php +++ b/tor_extension.php @@ -1,319 +1,445 @@ <?php /** - * Tor Extension for YAMN Web Interface + * Secure Tor Extension for YAMN Web Interface * - * This file provides functions to enable Tor connectivity for YAMN - * using torsocks to force all connections through Tor. + * Security Features: + * - No metadata retention (no detailed logging) + * - Randomized timing delays + * - Secure command execution + * - Tor enforcement with verification + * - Protection against timing attacks + * + * This implementation follows privacy-first principles */ // Constants define('YAMN_PATH', '/opt/yamn-master/yamn'); define('YAMN_CONFIG', '/opt/yamn-master/yamn.yml'); -define('TOR_PROXY', '127.0.0.1:9050'); // Default Tor SOCKS proxy -define('TOR_CONFIG_FILE', '/var/www/yamnweb/tor_config.ini'); -define('TORSOCKS_PATH', 'torsocks'); // Path to torsocks command (assuming it's in PATH) - -/** - * Debug logger function - * - * @param string $message Message to log - * @param bool $include_timestamp Whether to include timestamp - * @return void - */ -function logDebug($message, $include_timestamp = true) { - $log_file = '/var/www/yamnweb/debug.log'; - $log_entry = ($include_timestamp ? date('Y-m-d H:i:s') . " - " : "") . $message . "\n"; - file_put_contents($log_file, $log_entry, FILE_APPEND); -} +define('TOR_PROXY', '127.0.0.1:9050'); +define('TORSOCKS_PATH', 'torsocks'); +define('SECURE_POOL_DIR', '/opt/yamn-data/pool'); /** - * Check if Tor is available on the system + * Check if Tor is available and working * - * @return bool True if Tor service is available + * @return bool True if Tor is available */ function isTorAvailable() { - logDebug("Checking if Tor is available..."); - // Check if torsocks is installed - $torsocks_check = shell_exec('which torsocks 2>&1'); - if (empty($torsocks_check) || strpos($torsocks_check, 'no torsocks') !== false) { - logDebug("WARNING: torsocks not found in PATH. Tor routing may not work correctly."); - } else { - logDebug("torsocks found: " . trim($torsocks_check)); + $torsocksPath = shell_exec('which torsocks 2>/dev/null'); + if (empty($torsocksPath)) { + return false; } // Check if Tor SOCKS proxy is listening - $connection = @fsockopen('127.0.0.1', 9050, $errno, $errstr, 1); - if (is_resource($connection)) { - fclose($connection); - logDebug("Tor is available: SOCKS proxy connection successful"); - return true; + $connection = @fsockopen('127.0.0.1', 9050, $errno, $errstr, 2); + if (!is_resource($connection)) { + return false; } - logDebug("SOCKS proxy connection failed: $errno - $errstr"); + fclose($connection); + + // Verify Tor by checking external IP through Tor + // This ensures Tor is actually working, not just listening + $ch = curl_init(); + curl_setopt_array($ch, [ + CURLOPT_URL => 'https://check.torproject.org/api/ip', + CURLOPT_RETURNTRANSFER => true, + CURLOPT_PROXY => '127.0.0.1:9050', + CURLOPT_PROXYTYPE => CURLPROXY_SOCKS5_HOSTNAME, + CURLOPT_TIMEOUT => 30, + CURLOPT_SSL_VERIFYPEER => true + ]); + + $result = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); - // Alternative: Check if Tor service is running - $tor_running = shell_exec('pgrep -f "tor" | wc -l'); - if (intval($tor_running) > 0) { - logDebug("Tor is available: Tor process is running"); - return true; + if ($httpCode === 200 && $result) { + $data = json_decode($result, true); + return isset($data['IsTor']) && $data['IsTor'] === true; } - logDebug("Tor is NOT available: No Tor process found"); return false; } /** - * Check if an email address belongs to an .onion domain + * Secure random delay to prevent timing correlation * - * @param string $email The email address to check - * @return bool True if the email is for an .onion domain + * @param int $minSeconds Minimum delay in seconds + * @param int $maxSeconds Maximum delay in seconds */ -function isOnionEmail($email) { - // Extract domain from email address - if (preg_match('/@([^>]+)/', $email, $matches)) { - $domain = $matches[1]; - $is_onion = (strpos($domain, '.onion') !== false); - logDebug("Email domain check: $domain - " . ($is_onion ? "is .onion" : "is NOT .onion")); - return $is_onion; - } - logDebug("No domain found in email: $email"); - return false; +function secureRandomDelay($minSeconds = 10, $maxSeconds = 120) { + $delay = random_int($minSeconds, $maxSeconds); + sleep($delay); } /** - * Determine if Tor should be used for the current email - * Always returns true since Tor is now mandatory + * Validate remailer chain to prevent injection * - * @param string $to The recipient email address - * @return bool Always returns true + * @param string $chain The remailer chain + * @return bool True if valid */ -function shouldUseTor($to) { - logDebug("Checking if Tor should be used for recipient: $to"); - logDebug("Tor usage is mandatory for all communications"); - return true; +function isValidChain($chain) { + // Chain should only contain alphanumeric, comma, asterisk, and hyphen + return preg_match('/^[a-zA-Z0-9,\*\-]+$/', $chain) === 1; } /** - * Configure environment variables for Tor + * Secure command execution with proper escaping * - * @return void + * @param string $command Command to execute + * @param array &$output Output array + * @param int &$returnVar Return code */ -function configureTorForYamn() { - logDebug("Configuring environment for YAMN with Tor (mandatory)"); - - // Set environment variables that torsocks and YAMN might check for Tor configuration - putenv("SOCKS_PROXY=" . TOR_PROXY); - putenv("ALL_PROXY=socks5://" . TOR_PROXY); - putenv("socks_proxy=" . TOR_PROXY); - putenv("all_proxy=socks5://" . TOR_PROXY); - putenv("TORSOCKS_ALLOW_INBOUND=1"); // Allow inbound connections through torsocks - - logDebug("Environment variables set for Tor usage"); - - // Display current environment settings - logDebug("Current environment settings:"); - logDebug("SOCKS_PROXY: " . getenv("SOCKS_PROXY"), false); - logDebug("ALL_PROXY: " . getenv("ALL_PROXY"), false); - logDebug("TORSOCKS_ALLOW_INBOUND: " . getenv("TORSOCKS_ALLOW_INBOUND"), false); +function secureExec($command, &$output, &$returnVar) { + // Execute with output buffering + exec($command, $output, $returnVar); } /** - * Send an email through YAMN with Tor routing using torsocks + * Send email through YAMN with Tor (always enforced) * - * @param string $chain The remailer chain string - * @param int $copies Number of copies to send - * @param string $messageFile Path to the message file - * @param bool $useTor Parameter kept for compatibility, but ignored (Tor is always used) - * @return array Array containing success status and output + * @param string $chain Remailer chain + * @param int $copies Number of copies + * @param string $messageFile Path to message file + * @param bool $useTor Ignored, Tor is always used + * @return array Result status */ -function sendYamnEmail($chain, $copies, $messageFile, $useTor = true) { - logDebug("=== STARTING EMAIL SEND PROCESS ==="); - logDebug("Chain: $chain"); - logDebug("Copies: $copies"); - logDebug("Message file: $messageFile"); - logDebug("Using Tor: Yes (mandatory with torsocks)"); +function sendYamnEmailSecure($chain, $copies, $messageFile, $useTor = true) { + // Validate inputs + if (!isValidChain($chain)) { + return [ + 'success' => false, + 'errors' => ['Invalid remailer chain format'] + ]; + } - // Check if message file exists - if (!file_exists($messageFile)) { - logDebug("ERROR: Message file does not exist: $messageFile"); + if ($copies < 1 || $copies > 3) { return [ 'success' => false, - 'tor_used' => true, - 'add_to_pool' => [ - 'command' => '', - 'output' => ['Message file not found'], - 'return_var' => 1 - ], - 'send' => [ - 'command' => '', - 'output' => ['Skipped due to previous error'], - 'return_var' => 1 - ] + 'errors' => ['Invalid number of copies'] ]; } - // Check if YAMN exists and is executable - if (!file_exists(YAMN_PATH)) { - logDebug("ERROR: YAMN executable not found at: " . YAMN_PATH); + // Verify prerequisites + if (!file_exists($messageFile)) { return [ 'success' => false, - 'tor_used' => true, - 'add_to_pool' => [ - 'command' => '', - 'output' => ['YAMN executable not found'], - 'return_var' => 1 - ], - 'send' => [ - 'command' => '', - 'output' => ['Skipped due to previous error'], - 'return_var' => 1 - ] + 'errors' => ['Message file not found'] ]; } - if (!is_executable(YAMN_PATH)) { - logDebug("ERROR: YAMN is not executable: " . YAMN_PATH); + if (!file_exists(YAMN_PATH) || !is_executable(YAMN_PATH)) { return [ 'success' => false, - 'tor_used' => true, - 'add_to_pool' => [ - 'command' => '', - 'output' => ['YAMN is not executable'], - 'return_var' => 1 - ], - 'send' => [ - 'command' => '', - 'output' => ['Skipped due to previous error'], - 'return_var' => 1 - ] + 'errors' => ['YAMN executable not available'] ]; } - // Check if YAMN config exists if (!file_exists(YAMN_CONFIG)) { - logDebug("ERROR: YAMN config not found at: " . YAMN_CONFIG); return [ 'success' => false, - 'tor_used' => true, - 'add_to_pool' => [ - 'command' => '', - 'output' => ['YAMN config not found'], - 'return_var' => 1 - ], - 'send' => [ - 'command' => '', - 'output' => ['Skipped due to previous error'], - 'return_var' => 1 - ] + 'errors' => ['YAMN configuration not found'] ]; } - // Check if torsocks is available - $torsocks_check = shell_exec('which torsocks 2>/dev/null'); - if (empty($torsocks_check)) { - logDebug("ERROR: torsocks not found. Cannot route traffic through Tor."); + // Verify torsocks is available + $torsocksCheck = shell_exec('which torsocks 2>/dev/null'); + if (empty($torsocksCheck)) { return [ 'success' => false, - 'tor_used' => true, - 'add_to_pool' => [ - 'command' => '', - 'output' => ['torsocks not installed'], - 'return_var' => 1 - ], - 'send' => [ - 'command' => '', - 'output' => ['Skipped due to previous error'], - 'return_var' => 1 - ] + 'errors' => ['torsocks not installed'] ]; } - // Configure environment for torsocks - configureTorForYamn(); - - // Build YAMN command to add message to pool with torsocks - $command_add_to_pool = TORSOCKS_PATH . " " . YAMN_PATH . " " . - "--config=" . YAMN_CONFIG . " " . - "--mail " . - "--chain=\"$chain\" " . - "--copies=$copies < $messageFile 2>&1"; - - // Command to send emails in pool with torsocks - $command_send = TORSOCKS_PATH . " " . YAMN_PATH . " " . - "--config=" . YAMN_CONFIG . " -S 2>&1"; - - // Log the commands - logDebug("Add to pool command: $command_add_to_pool"); - - // Execute add to pool command - logDebug("Executing add to pool command..."); - exec($command_add_to_pool, $output_add_to_pool, $return_var_add_to_pool); - - // Log the output and return code - logDebug("Add to pool output: " . implode("\n", $output_add_to_pool)); - logDebug("Add to pool return code: $return_var_add_to_pool"); - - // Only execute send command if add to pool was successful - if ($return_var_add_to_pool == 0) { - logDebug("Send command: $command_send"); - logDebug("Executing send command..."); - exec($command_send, $output_send, $return_var_send); - logDebug("Send output: " . implode("\n", $output_send)); - logDebug("Send return code: $return_var_send"); - } else { - logDebug("Skipping send command due to add to pool failure"); - $output_send = ["Skipped - add to pool command failed"]; - $return_var_send = -1; + // Random delay BEFORE adding to pool (prevent timing correlation) + secureRandomDelay(5, 30); + + // Escape arguments properly + $escapedChain = escapeshellarg($chain); + $escapedCopies = (int)$copies; + $escapedMessageFile = escapeshellarg($messageFile); + $escapedYamnPath = escapeshellarg(YAMN_PATH); + $escapedConfig = escapeshellarg(YAMN_CONFIG); + + // Build command with proper escaping + // Use process substitution to avoid leaving message file readable + $commandAddToPool = sprintf( + '%s %s --config=%s --mail --chain=%s --copies=%d 2>&1 < %s', + TORSOCKS_PATH, + $escapedYamnPath, + $escapedConfig, + $escapedChain, + $escapedCopies, + $escapedMessageFile + ); + + // Execute add to pool + $outputAddToPool = []; + $returnVarAddToPool = 0; + secureExec($commandAddToPool, $outputAddToPool, $returnVarAddToPool); + + // Check if add to pool succeeded + if ($returnVarAddToPool !== 0) { + return [ + 'success' => false, + 'errors' => ['Failed to add message to pool'] + ]; } - // Standard log entry - $log_entry = date('Y-m-d H:i:s') . " | Chain: $chain | Copies: $copies | Tor: Yes | Status: " . - (($return_var_send == 0 && $return_var_add_to_pool == 0) ? "Success" : "Failed") . "\n"; - file_put_contents('/var/www/yamnweb/yamn_send.log', $log_entry, FILE_APPEND); - - // Success status - $success = ($return_var_send == 0 && $return_var_add_to_pool == 0); - logDebug("Operation " . ($success ? "SUCCEEDED" : "FAILED")); - logDebug("=== EMAIL SEND PROCESS COMPLETE ==="); - - return [ - 'success' => $success, - 'tor_used' => true, - 'add_to_pool' => [ - 'command' => $command_add_to_pool, - 'output' => $output_add_to_pool, - 'return_var' => $return_var_add_to_pool - ], - 'send' => [ - 'command' => $command_send, - 'output' => $output_send, - 'return_var' => $return_var_send - ] - ]; + // Random delay BETWEEN operations + secureRandomDelay(3, 15); + + // Build send command + $commandSend = sprintf( + '%s %s --config=%s -S 2>&1', + TORSOCKS_PATH, + $escapedYamnPath, + $escapedConfig + ); + + // Execute send + $outputSend = []; + $returnVarSend = 0; + secureExec($commandSend, $outputSend, $returnVarSend); + + // Determine success + $success = ($returnVarSend === 0 && $returnVarAddToPool === 0); + + // Random delay AFTER sending (prevent timing correlation) + secureRandomDelay(2, 10); + + // Return minimal information (no detailed logs) + if ($success) { + return [ + 'success' => true + ]; + } else { + return [ + 'success' => false, + 'errors' => ['Failed to send message'] + ]; + } } /** - * Alternative version that uses proxychains4 instead of torsocks - * Uncomment and modify the above function if you prefer proxychains4 + * Legacy wrapper for backward compatibility + * Always uses Tor regardless of $useTor parameter */ -/* function sendYamnEmail($chain, $copies, $messageFile, $useTor = true) { - // Configuration similar to above, but using proxychains4 - // ... + return sendYamnEmailSecure($chain, $copies, $messageFile, true); +} + +/** + * Verify Tor circuit and get new identity + * Useful before sending to ensure fresh circuit + * + * @return bool True if successful + */ +function getTorNewIdentity() { + // Connect to Tor control port + $fp = @fsockopen('127.0.0.1', 9051, $errno, $errstr, 5); + if (!$fp) { + return false; + } + + // Authenticate (assuming no password or cookie auth) + // In production, use proper authentication + fwrite($fp, "AUTHENTICATE\r\n"); + $response = fgets($fp); + + if (strpos($response, '250') === false) { + fclose($fp); + return false; + } + + // Request new identity + fwrite($fp, "SIGNAL NEWNYM\r\n"); + $response = fgets($fp); + + fclose($fp); + + return strpos($response, '250') !== false; +} + +/** + * Enhanced Tor check with circuit verification + * + * @return bool True if Tor is working properly + */ +function verifyTorCircuit() { + // Check basic Tor availability + if (!isTorAvailable()) { + return false; + } + + // Try to get a new circuit + // This ensures Tor is not just running but actually functional + if (!getTorNewIdentity()) { + // If we can't get new identity, Tor might still work + // Don't fail, just continue + } + + // Wait a moment for circuit to build + sleep(2); + + return true; +} + +/** + * Initialize secure environment for YAMN operations + * + * @return bool True if successful + */ +function initSecureEnvironment() { + // Ensure secure directories exist + if (!file_exists(SECURE_POOL_DIR)) { + mkdir(SECURE_POOL_DIR, 0700, true); + } + + // Set restrictive umask + umask(0077); + + // Verify Tor is available + if (!isTorAvailable()) { + return false; + } + + return true; +} + +/** + * Clean up function to be called after operations + * Removes temporary files securely + * + * @param string $filepath File to securely delete + */ +function secureCleanup($filepath) { + if (!file_exists($filepath)) { + return; + } + + $filesize = filesize($filepath); + + // DoD 5220.22-M standard: 3-pass overwrite + for ($i = 0; $i < 3; $i++) { + $fp = fopen($filepath, 'w'); + if ($fp) { + fwrite($fp, random_bytes($filesize)); + fclose($fp); + } + } + + // Final overwrite with zeros + $fp = fopen($filepath, 'w'); + if ($fp) { + fwrite($fp, str_repeat("\0", $filesize)); + fclose($fp); + } + + // Delete file + unlink($filepath); +} + +/** + * Get Tor circuit information (for debugging only) + * DO NOT use in production as it may leak information + * + * @return array|false Circuit info or false + */ +function getTorCircuitInfo() { + // Only enable if explicitly requested via environment variable + if (getenv('YAMN_DEBUG_TOR') !== 'true') { + return false; + } + + $fp = @fsockopen('127.0.0.1', 9051, $errno, $errstr, 5); + if (!$fp) { + return false; + } + + fwrite($fp, "AUTHENTICATE\r\n"); + $response = fgets($fp); + + if (strpos($response, '250') === false) { + fclose($fp); + return false; + } + + fwrite($fp, "GETINFO circuit-status\r\n"); + $response = ''; + while (!feof($fp)) { + $line = fgets($fp); + $response .= $line; + if (strpos($line, '250 OK') !== false) { + break; + } + } + + fclose($fp); + + return ['circuit_status' => $response]; +} + +/** + * Test Tor connectivity without leaving traces + * + * @return array Test results + */ +function testTorConnectivity() { + $results = [ + 'tor_available' => false, + 'socks_proxy' => false, + 'circuit_working' => false, + 'torsocks_installed' => false + ]; - // Build YAMN command with proxychains4 - $command_add_to_pool = "proxychains4 -q " . YAMN_PATH . " " . - "--config=" . YAMN_CONFIG . " " . - "--mail " . - "--chain=\"$chain\" " . - "--copies=$copies < $messageFile 2>&1"; + // Check SOCKS proxy + $connection = @fsockopen('127.0.0.1', 9050, $errno, $errstr, 2); + if (is_resource($connection)) { + $results['socks_proxy'] = true; + fclose($connection); + } - $command_send = "proxychains4 -q " . YAMN_PATH . " " . - "--config=" . YAMN_CONFIG . " -S 2>&1"; + // Check torsocks + $torsocksPath = shell_exec('which torsocks 2>/dev/null'); + $results['torsocks_installed'] = !empty($torsocksPath); - // Rest of the function is the same - // ... + // Check if Tor is actually working + $ch = curl_init(); + curl_setopt_array($ch, [ + CURLOPT_URL => 'https://check.torproject.org/api/ip', + CURLOPT_RETURNTRANSFER => true, + CURLOPT_PROXY => '127.0.0.1:9050', + CURLOPT_PROXYTYPE => CURLPROXY_SOCKS5_HOSTNAME, + CURLOPT_TIMEOUT => 15, + CURLOPT_SSL_VERIFYPEER => true + ]); + + $result = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($httpCode === 200 && $result) { + $data = json_decode($result, true); + if (isset($data['IsTor']) && $data['IsTor'] === true) { + $results['circuit_working'] = true; + } + } + + $results['tor_available'] = ( + $results['socks_proxy'] && + $results['torsocks_installed'] && + $results['circuit_working'] + ); + + return $results; +} + +// Initialize on load +if (!initSecureEnvironment()) { + // Silent fail - don't leak information + // In production, handle this appropriately } -*/ ?> |
