public
nobgit
read
NobMail
Based on mailcow: dockerized
Languages
Repository composition by tracked source files.
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
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
-}
-?>