Issue Description
What happened?
Adding an IMAP account in Outlook for Windows (classic) via Autodiscover never completes. Outlook retrieves the settings from Stalwart (POST /autodiscover/autodiscover.xml → HTTP 200). The setup wizard then stalls for a long time and finally reports that the account could not be configured.
For the implicit-TLS ports, the response contains both <SSL>on</SSL> and <Encryption>TLS</Encryption>:
<Protocol>
<Type>IMAP</Type>
<Server>mail.example.com</Server>
<Port>993</Port>
<LoginName>[email protected]</LoginName>
<AuthRequired>on</AuthRequired>
<DirectoryPort>0</DirectoryPort>
<ReferralPort>0</ReferralPort>
<SSL>on</SSL>
<Encryption>TLS</Encryption>
<SPA>off</SPA>
</Protocol>
[MS-OXDSCLI] states: “If the Encryption element is present, it overrides the SSL element.” Its allowed values are None, SSL, TLS and Auto. These are the same four options as the encryption drop-down in Outlook’s manual IMAP/SMTP settings, where current builds label them SSL/TLS (implicit TLS) and STARTTLS. So Encryption=TLS tells Outlook to use STARTTLS on 993/995/465.
Outlook’s Test E-mail AutoConfiguration tool shows the IMAP entry for port 993 as TLS: Yes.
Outlook therefore opens a plaintext connection to 993/465 and waits for the server greeting before sending STARTTLS. Stalwart, meanwhile, waits for a TLS ClientHello. Neither side sends anything until Outlook times out.
Confirmed: with a reverse proxy rewriting <Encryption>TLS</Encryption> to <Encryption>SSL</Encryption> in this response, Outlook completes the account setup automatically.
How can we reproduce the problem?
- Run Stalwart with implicit-TLS listeners for IMAP (993) and submission (465), with
cleartextdisabled for these services. - Request the Autodiscover settings:
curl -s -X POST https://autodiscover.example.com/autodiscover/autodiscover.xml \
-H 'Content-Type: text/xml' \
--data '<?xml version="1.0" encoding="utf-8"?><Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006"><Request><EMailAddress>[email protected]</EMailAddress><AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema></Request></Autodiscover>'
Every <Protocol> block for an implicit-TLS port (993, 995, 465) contains <SSL>on</SSL> and <Encryption>TLS</Encryption>.
3. Emulate what Outlook does with that value (a plaintext connection that waits for a greeting):
nc -v mail.example.com 993 # connects; no greeting ever arrives
openssl s_client -connect mail.example.com:993 -quiet # implicit TLS: "* OK ..." greeting arrives immediately
- In Outlook (classic, Windows), go to File → Add Account and enter the address. The wizard hangs, then fails. Ctrl + right-click the Outlook tray icon → Test E-mail AutoConfiguration shows IMAP port 993 with “TLS: Yes”.
With the same credentials, implicit TLS on the same ports works: IMAP LOGIN/LIST/SELECT and SMTP AUTH/MAIL FROM/RCPT TO succeed. A plaintext connection to 993/465, which is what Outlook opens for Encryption=TLS, receives no greeting within 20 s.
Root cause
crates/common/src/network/autoconfig/autodiscover.rs hardcodes Encryption=TLS for the implicit-TLS port and emits no <Encryption> for the plaintext port (lines 58–89 at commit 9d1c75a):
There is no configuration workaround: Service (in crates/registry/src/schema/structs.rs) only exposes hostname and cleartext.
Enabling cleartext makes it worse. The plaintext ports (143/110/587) are listed first, with <SSL>off</SSL> and no <Encryption>, which means no encryption at all for Outlook.
The Mozilla autoconfig endpoint already maps this correctly: SSL for the TLS port and STARTTLS for the plaintext port (crates/common/src/network/autoconfig/legacy_autoconfig.rs, lines 49–58 at the same commit). Only Autodiscover is affected.
Proposed fix
--- a/crates/common/src/network/autoconfig/autodiscover.rs
+++ b/crates/common/src/network/autoconfig/autodiscover.rs
@@ -77,8 +77,9 @@
- let _ = writeln!(
- &mut config,
- "\t\t\t\t<SSL>{}</SSL>",
- if is_tls == 1 { "on" } else { "off" }
- );
- if is_tls == 1 {
- let _ = writeln!(&mut config, "\t\t\t\t<Encryption>TLS</Encryption>");
- }
+ // MS-OXDSCLI: <Encryption> overrides <SSL>. For Outlook, "SSL" means
+ // implicit TLS and "TLS" means STARTTLS.
+ let (ssl, encryption) = if is_tls == 1 {
+ ("on", "SSL")
+ } else {
+ ("off", "TLS")
+ };
+ let _ = writeln!(&mut config, "\t\t\t\t<SSL>{ssl}</SSL>");
+ let _ = writeln!(&mut config, "\t\t\t\t<Encryption>{encryption}</Encryption>");
Suggestion: when cleartext is enabled, emit the implicit-TLS block before the STARTTLS one, so that clients taking the first block prefer implicit TLS (RFC 8314).
Workaround
Rewrite the response in a reverse proxy. Tested in production with Caddy and the caddyserver/replace-response plugin:
@autodiscover_xml path /autodiscover/autodiscover.xml /autodiscover.xml
handle {
request_header @autodiscover_xml Accept-Encoding identity
replace @autodiscover_xml "<Encryption>TLS</Encryption>" "<Encryption>SSL</Encryption>"
reverse_proxy 127.0.0.1:8080
}
Accept-Encoding identitykeeps the body uncompressed so the text can be rewritten.- The matcher keeps the rewrite away from other paths, such as WebDAV file content.
- Apply it to both
autodiscover.<domain>and the server hostname, because the Autodiscover V2 JSON response points clients tohttps://<server-hostname>/autodiscover/autodiscover.xml.
Alternatively, set up accounts manually in Outlook with SSL/TLS on 993 and 465.
Environment
- Stalwart: v0.16.22 (Docker image
stalwartlabs/stalwart:v0.16.22); the code above is unchanged onmainat 9d1c75a - Outlook (classic), Microsoft 365 Apps Click-to-Run x64, build 16.0.17932.21000, Windows 11 (build 26100)
- Caddy in front of Stalwart for HTTPS. The problem was reproduced with the XML passed through unchanged; the workaround above is the only rewrite.
References
- [MS-OXDSCLI] Autodiscover Publishing and Lookup Protocol, Encryption element (§2.2.4.1.1.2.4.22 in rev. 2018-07-24): https://officeprotocoldoc.z19.web.core.windows.net/files/MS-OXDSCLI/[MS-OXDSCLI]-180724.pdf
virtualmin/virtualmin-gplissue 764: the mirror-image bug (port 587 advertised as SSL instead of STARTTLS).gronke/email-autodiscoverpull request 3: uses<Encryption>TLS</Encryption>for STARTTLS submission.
Stalwart Version
v0.16.x
Installation Method
Docker
Database Backend
RocksDB
Blob Storage
S3-compatible
Search Engine
Internal
Directory Backend
Internal
I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.
on
I have searched this support forum (open and closed topics) and confirm this is not a duplicate.
on
I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.
on