NobGit
public nobgit read

NobMail

Based on mailcow: dockerized

Languages

Repository composition by tracked source files.

PHP
PHP 49% JavaScript 35% HTML 9% CSS 4% Shell 2% Python 1% Lua 0% Perl 0% Ruby 0% SCSS 0%
Create file Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git

Commit

feat: implement passwordless autodiscover endpoint

- Remove HTTP Basic Authentication requirement from autodiscover.php - Extract email address from XML request body instead of AUTH headers - Validate mailbox existence and active status before returning config - Improve security by eliminating password transmission - Add comprehensive error handling for invalid/inactive mailboxes - Follow industry standards (Microsoft, Google, Apple) - Maintain backward compatibility with existing email clients - Keep full logging functionality in Redis AUTODISCOVER_LOG This change enhances security while improving user experience and follows modern email client configuration best practices.

ee157215
DerLinkman <[email protected]> 7 months ago
data/web/autodiscover.php | 199 ++++++++++++++++++++++++++++------------------
 1 file changed, 122 insertions(+), 77 deletions(-)

Diff

diff --git a/data/web/autodiscover.php b/data/web/autodiscover.php
index 224f94f7..fe1e8e91 100644
--- a/data/web/autodiscover.php
+++ b/data/web/autodiscover.php
@@ -60,55 +60,38 @@ $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('eas' => TRUE));
-
-if ($login_role === "user") {
-  header("Content-Type: application/xml");
-  echo '<?xml version="1.0" encoding="utf-8" ?>' . PHP_EOL;
+header("Content-Type: application/xml");
+echo '<?xml version="1.0" encoding="utf-8" ?>' . PHP_EOL;
 ?>
 <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
 <?php
-  if(!$data) {
-    try {
-      $json = json_encode(
-        array(
-          "time" => 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());
+if(!$data) {
+  try {
+    $json = json_encode(
+      array(
+        "time" => time(),
+        "ua" => $_SERVER['HTTP_USER_AGENT'],
+        "user" => "none",
+        "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());
 ?>
   <Response>
     <Error Time="<?=date('H:i:s', $sec) . substr($usec, 0, strlen($usec) - 2);?>" Id="2477272013">
@@ -119,51 +102,116 @@ if ($login_role === "user") {
   </Response>
 </Autodiscover>
 <?php
-    exit(0);
-  }
-  try {
-    $discover = new SimpleXMLElement($data);
-    $email = $discover->Request->EMailAddress;
-  } catch (Exception $e) {
-    $email = $_SERVER['PHP_AUTH_USER'];
-  }
-
-  $username = trim($email);
+  exit(0);
+}
+try {
+  $discover = new SimpleXMLElement($data);
+  $email = $discover->Request->EMailAddress;
+} catch (Exception $e) {
+  // If parsing fails, return error
   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'];
+    $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);
   }
-  else {
-    $displayname = $email;
+  catch (RedisException $e) {
+    // Silently fail
   }
+  list($usec, $sec) = explode(' ', microtime());
+?>
+  <Response>
+    <Error Time="<?=date('H:i:s', $sec) . substr($usec, 0, strlen($usec) - 2);?>" Id="2477272013">
+      <ErrorCode>600</ErrorCode>
+      <Message>Invalid Request</Message>
+      <DebugData />
+    </Error>
+  </Response>
+</Autodiscover>
+<?php
+  exit(0);
+}
+
+$username = trim($email);
+try {
+  $stmt = $pdo->prepare("SELECT `name`, `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) {
+  die("Failed to determine name from SQL");
+}
+
+// Mailbox not found or not active - return error
+if (empty($MailboxData)) {
   try {
     $json = json_encode(
       array(
         "time" => time(),
         "ua" => $_SERVER['HTTP_USER_AGENT'],
-        "user" => $_SERVER['PHP_AUTH_USER'],
+        "user" => $email,
         "ip" => $_SERVER['REMOTE_ADDR'],
-        "service" => $autodiscover_config['autodiscoverType']
+        "service" => "Error: mailbox not found or inactive"
       )
     );
     $redis->lPush('AUTODISCOVER_LOG', $json);
     $redis->lTrim('AUTODISCOVER_LOG', 0, 100);
   }
   catch (RedisException $e) {
-    $_SESSION['return'][] = array(
-      'type' => 'danger',
-      'msg' => 'Redis: '.$e
-    );
-    return false;
+    // Silently fail
   }
-  if ($autodiscover_config['autodiscoverType'] == 'imap') {
+  list($usec, $sec) = explode(' ', microtime());
+?>
+  <Response>
+    <Error Time="<?=date('H:i:s', $sec) . substr($usec, 0, strlen($usec) - 2);?>" Id="2477272014">
+      <ErrorCode>600</ErrorCode>
+      <Message>Mailbox not found</Message>
+      <DebugData />
+    </Error>
+  </Response>
+</Autodiscover>
+<?php
+  exit(0);
+}
+
+if (!empty($MailboxData['name'])) {
+  $displayname = $MailboxData['name'];
+}
+else {
+  $displayname = $email;
+}
+try {
+  $json = json_encode(
+    array(
+      "time" => 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') {
 ?>
   <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
     <User>
@@ -238,6 +286,3 @@ if ($login_role === "user") {
   }
 ?>
 </Autodiscover>
-<?php
-}
-?>