diff --git a/data/web/autodiscover.php b/data/web/autodiscover.php index d3cda4004..3e31dc3dd 100644 --- a/data/web/autodiscover.php +++ b/data/web/autodiscover.php @@ -60,101 +60,31 @@ $pdo = new PDO($dsn, $database_user, $database_pass, $opt); $iam_provider = identity_provider('init'); $iam_settings = identity_provider('get'); -$login_user = strtolower(trim($_SERVER['PHP_AUTH_USER'])); -$login_pass = trim(htmlspecialchars_decode($_SERVER['PHP_AUTH_PW'])); +// Passwordless autodiscover - no authentication required +// Email will be extracted from the request body +$login_user = null; +$login_role = null; -if (empty($_SERVER['PHP_AUTH_USER']) || empty($_SERVER['PHP_AUTH_PW'])) { - $json = json_encode( - array( - "time" => time(), - "ua" => $_SERVER['HTTP_USER_AGENT'], - "user" => "none", - "ip" => $_SERVER['REMOTE_ADDR'], - "service" => "Error: must be authenticated" - ) - ); - $redis->lPush('AUTODISCOVER_LOG', $json); - header('WWW-Authenticate: Basic realm="' . $_SERVER['HTTP_HOST'] . '"'); - header('HTTP/1.0 401 Unauthorized'); - exit(0); -} - -$login_role = check_login($login_user, $login_pass, array('service' => 'EAS')); - -if ($login_role === "user") { - header("Content-Type: application/xml"); - echo '' . PHP_EOL; +header("Content-Type: application/xml"); +echo '' . PHP_EOL; ?> time(), - "ua" => $_SERVER['HTTP_USER_AGENT'], - "user" => $_SERVER['PHP_AUTH_USER'], - "ip" => $_SERVER['REMOTE_ADDR'], - "service" => "Error: invalid or missing request data" - ) - ); - $redis->lPush('AUTODISCOVER_LOG', $json); - $redis->lTrim('AUTODISCOVER_LOG', 0, 100); - } - catch (RedisException $e) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'msg' => 'Redis: '.$e - ); - return false; - } - list($usec, $sec) = explode(' ', microtime()); -?> - - - 600 - Invalid Request - - - - -Request->EMailAddress; - } catch (Exception $e) { - $email = $_SERVER['PHP_AUTH_USER']; - } - - $username = trim($email); - try { - $stmt = $pdo->prepare("SELECT `name` FROM `mailbox` WHERE `username`= :username"); - $stmt->execute(array(':username' => $username)); - $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC); - } - catch(PDOException $e) { - die("Failed to determine name from SQL"); - } - if (!empty($MailboxData['name'])) { - $displayname = $MailboxData['name']; - } - else { - $displayname = $email; - } +if(!$data) { try { $json = json_encode( array( "time" => time(), "ua" => $_SERVER['HTTP_USER_AGENT'], - "user" => $_SERVER['PHP_AUTH_USER'], + "user" => "none", "ip" => $_SERVER['REMOTE_ADDR'], - "service" => $autodiscover_config['autodiscoverType'] + "service" => "Error: invalid or missing request data" ) ); $redis->lPush('AUTODISCOVER_LOG', $json); $redis->lTrim('AUTODISCOVER_LOG', 0, 100); + $redis->publish("F2B_CHANNEL", "Autodiscover: Invalid request by " . $_SERVER['REMOTE_ADDR']); + error_log("Autodiscover: Invalid request by " . $_SERVER['REMOTE_ADDR']); } catch (RedisException $e) { $_SESSION['return'][] = array( @@ -163,7 +93,143 @@ if ($login_role === "user") { ); return false; } - if ($autodiscover_config['autodiscoverType'] == 'imap') { + list($usec, $sec) = explode(' ', microtime()); +?> + + + 600 + Invalid Request + + + + +Request->EMailAddress; +} catch (Exception $e) { + // If parsing fails, return error + try { + $json = json_encode( + array( + "time" => time(), + "ua" => $_SERVER['HTTP_USER_AGENT'], + "user" => "none", + "ip" => $_SERVER['REMOTE_ADDR'], + "service" => "Error: could not parse email from request" + ) + ); + $redis->lPush('AUTODISCOVER_LOG', $json); + $redis->lTrim('AUTODISCOVER_LOG', 0, 100); + $redis->publish("F2B_CHANNEL", "Autodiscover: Malformed XML by " . $_SERVER['REMOTE_ADDR']); + error_log("Autodiscover: Malformed XML by " . $_SERVER['REMOTE_ADDR']); + } + catch (RedisException $e) { + // Silently fail + } + list($usec, $sec) = explode(' ', microtime()); +?> + + + 600 + Invalid Request + + + + +prepare("SELECT `mailbox`.`name`, `mailbox`.`active` FROM `mailbox` + INNER JOIN `domain` ON `mailbox`.`domain` = `domain`.`domain` + WHERE `mailbox`.`username` = :username + AND `mailbox`.`active` = '1' + AND `domain`.`active` = '1'"); + $stmt->execute(array(':username' => $username)); + $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC); +} +catch(PDOException $e) { + // Database error - return error response with complete XML + list($usec, $sec) = explode(' ', microtime()); +?> + + + 500 + Database Error + + + + + time(), + "ua" => $_SERVER['HTTP_USER_AGENT'], + "user" => $email, + "ip" => $_SERVER['REMOTE_ADDR'], + "service" => "Error: mailbox not found or inactive" + ) + ); + $redis->lPush('AUTODISCOVER_LOG', $json); + $redis->lTrim('AUTODISCOVER_LOG', 0, 100); + $redis->publish("F2B_CHANNEL", "Autodiscover: Invalid mailbox attempt by " . $_SERVER['REMOTE_ADDR']); + error_log("Autodiscover: Invalid mailbox attempt by " . $_SERVER['REMOTE_ADDR']); + } + catch (RedisException $e) { + // Silently fail + } + list($usec, $sec) = explode(' ', microtime()); +?> + + + 600 + Invalid Request + + + + + time(), + "ua" => $_SERVER['HTTP_USER_AGENT'], + "user" => $email, + "ip" => $_SERVER['REMOTE_ADDR'], + "service" => $autodiscover_config['autodiscoverType'] + ) + ); + $redis->lPush('AUTODISCOVER_LOG', $json); + $redis->lTrim('AUTODISCOVER_LOG', 0, 100); +} +catch (RedisException $e) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'msg' => 'Redis: '.$e + ); + return false; +} +if ($autodiscover_config['autodiscoverType'] == 'imap') { ?> @@ -238,6 +304,3 @@ if ($login_role === "user") { } ?> - diff --git a/helper-scripts/dev_tests/view_autodiscover.sh b/helper-scripts/dev_tests/view_autodiscover.sh new file mode 100755 index 000000000..a203370de --- /dev/null +++ b/helper-scripts/dev_tests/view_autodiscover.sh @@ -0,0 +1,122 @@ +#!/bin/bash + +# Autodiscover XML Debug Script +# Usage: ./view_autodiscover.sh [OPTIONS] [email@domain.com] + +# Function to display help +show_help() { + cat << EOF +Autodiscover XML Debug Script + +Usage: $0 [OPTIONS] [email@domain.com] + +OPTIONS: + -h, --help Show this help message + -d, --domain FQDN Override autodiscover domain (default: autodiscover.DOMAIN) + Example: -d mail.example.com + +EXAMPLES: + $0 user@example.com + Test autodiscover for user@example.com using autodiscover.example.com + + $0 -d mail.example.com user@example.com + Test autodiscover for user@example.com using mail.example.com + + $0 -d localhost:8443 user@example.com + Test autodiscover using localhost:8443 (useful for development) + +EOF + exit 0 +} + +# Initialize variables +EMAIL="" +DOMAIN_OVERRIDE="" + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_help + ;; + -d|--domain) + DOMAIN_OVERRIDE="$2" + shift 2 + ;; + -*) + echo "Error: Unknown option $1" + echo "Use -h or --help for usage information" + exit 1 + ;; + *) + EMAIL="$1" + shift + ;; + esac +done + +# Check if xmllint is available +if ! command -v xmllint &> /dev/null; then + echo "WARNING: xmllint not found. Output will not be formatted." + echo "Install with: apt install libxml2-utils (Debian/Ubuntu) or yum install libxml2 (CentOS/RHEL)" + echo "" + USE_XMLLINT=false +else + USE_XMLLINT=true +fi + +# Get email address from user input if not provided +if [ -z "$EMAIL" ]; then + read -p "Enter email address to test: " EMAIL +fi + +# Validate email format +if [[ ! "$EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + echo "Error: Invalid email address format" + exit 1 +fi + +# Extract domain from email +EMAIL_DOMAIN="${EMAIL#*@}" + +# Determine autodiscover URL +if [ -n "$DOMAIN_OVERRIDE" ]; then + AUTODISCOVER_URL="https://${DOMAIN_OVERRIDE}/Autodiscover/Autodiscover.xml" + echo "Testing Autodiscover for: $EMAIL" + echo "Override domain: $DOMAIN_OVERRIDE" +else + AUTODISCOVER_URL="https://autodiscover.${EMAIL_DOMAIN}/Autodiscover/Autodiscover.xml" + echo "Testing Autodiscover for: $EMAIL" +fi + +echo "URL: $AUTODISCOVER_URL" +echo "============================================" +echo "" + +# Make the request +RESPONSE=$(curl -k -s -X POST "$AUTODISCOVER_URL" \ + -H "Content-Type: text/xml" \ + -d " + + + $EMAIL + http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a + +") + +# Check if response is empty +if [ -z "$RESPONSE" ]; then + echo "Error: No response received from server" + exit 1 +fi + +# Format and display output +if [ "$USE_XMLLINT" = true ]; then + echo "$RESPONSE" | xmllint --format - 2>&1 +else + echo "$RESPONSE" +fi + +echo "" +echo "============================================" +echo "Response length: ${#RESPONSE} bytes"