diff --git a/app/src/main/java/eu/faircode/email/EmailService.java b/app/src/main/java/eu/faircode/email/EmailService.java
index df5949281a..80ccca054d 100644
--- a/app/src/main/java/eu/faircode/email/EmailService.java
+++ b/app/src/main/java/eu/faircode/email/EmailService.java
@@ -63,6 +63,7 @@ import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivateKey;
+import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -104,6 +105,7 @@ public class EmailService implements AutoCloseable {
private Context context;
private String protocol;
private boolean insecure;
+ private boolean anchor;
private int purpose;
private boolean harden;
private boolean useip;
@@ -183,6 +185,7 @@ public class EmailService implements AutoCloseable {
prefs.edit().putBoolean("protocol", false).apply();
this.log = prefs.getBoolean("protocol", false);
this.level = prefs.getInt("log_level", Log.getDefaultLogLevel());
+ this.anchor = prefs.getBoolean("ssl_anchor", !BuildConfig.PLAY_STORE_RELEASE);
this.harden = prefs.getBoolean("ssl_harden", false);
boolean auth_plain = prefs.getBoolean("auth_plain", true);
@@ -404,7 +407,7 @@ public class EmailService implements AutoCloseable {
}
}
- factory = new SSLSocketFactoryService(host, insecure, harden, key, chain, fingerprint);
+ factory = new SSLSocketFactoryService(host, insecure, anchor, harden, key, chain, fingerprint);
properties.put("mail." + protocol + ".ssl.socketFactory", factory);
properties.put("mail." + protocol + ".socketFactory.fallback", "false");
properties.put("mail." + protocol + ".ssl.checkserveridentity", "false");
@@ -942,14 +945,16 @@ public class EmailService implements AutoCloseable {
// openssl s_client -connect host:port < /dev/null 2>/dev/null | openssl x509 -fingerprint -noout -in /dev/stdin
private String server;
private boolean secure;
+ private boolean anchor;
private boolean harden;
private String trustedFingerprint;
private SSLSocketFactory factory;
private X509Certificate certificate;
- SSLSocketFactoryService(String host, boolean insecure, boolean harden, PrivateKey key, X509Certificate[] chain, String fingerprint) throws GeneralSecurityException {
+ SSLSocketFactoryService(String host, boolean insecure, boolean anchor, boolean harden, PrivateKey key, X509Certificate[] chain, String fingerprint) throws GeneralSecurityException {
this.server = host;
this.secure = !insecure;
+ this.anchor = anchor;
this.harden = harden;
this.trustedFingerprint = fingerprint;
@@ -987,13 +992,23 @@ public class EmailService implements AutoCloseable {
// Check certificates
try {
+ Log.i("Auth type=" + authType);
rtm.checkServerTrusted(chain, authType);
} catch (CertificateException ex) {
Principal principal = certificate.getSubjectDN();
if (principal == null)
throw ex;
- else
- throw new CertificateException(principal.getName(), ex);
+ else {
+ if (ex.getCause() instanceof CertPathValidatorException &&
+ "Trust anchor for certification path not found."
+ .equals(ex.getCause().getMessage())) {
+ if (anchor)
+ throw new CertificateException(principal.getName(), ex);
+ else
+ Log.e(ex);
+ } else
+ throw new CertificateException(principal.getName(), ex);
+ }
}
// Check host name
diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsConnection.java b/app/src/main/java/eu/faircode/email/FragmentOptionsConnection.java
index fba3922bcf..dcb109b7ae 100644
--- a/app/src/main/java/eu/faircode/email/FragmentOptionsConnection.java
+++ b/app/src/main/java/eu/faircode/email/FragmentOptionsConnection.java
@@ -70,6 +70,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
private SwitchCompat swStandaloneVpn;
private SwitchCompat swTcpKeepAlive;
private TextView tvTcpKeepAliveHint;
+ private SwitchCompat swSslAnchor;
private SwitchCompat swSslHarden;
private Button btnManage;
private TextView tvNetworkMetered;
@@ -83,7 +84,8 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
"metered", "download", "roaming", "rlah",
"download_headers", "download_eml",
"require_validated", "vpn_only",
- "timeout", "prefer_ip4", "bind_socket", "standalone_vpn", "tcp_keep_alive", "ssl_harden"
+ "timeout", "prefer_ip4", "bind_socket", "standalone_vpn", "tcp_keep_alive",
+ "ssl_anchor", "ssl_harden"
};
@Override
@@ -110,6 +112,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
swStandaloneVpn = view.findViewById(R.id.swStandaloneVpn);
swTcpKeepAlive = view.findViewById(R.id.swTcpKeepAlive);
tvTcpKeepAliveHint = view.findViewById(R.id.tvTcpKeepAliveHint);
+ swSslAnchor = view.findViewById(R.id.swSslAnchor);
swSslHarden = view.findViewById(R.id.swSslHarden);
btnManage = view.findViewById(R.id.btnManage);
@@ -256,6 +259,13 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
}
});
+ swSslAnchor.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
+ prefs.edit().putBoolean("ssl_anchor", checked).apply();
+ }
+ });
+
swSslHarden.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@@ -373,6 +383,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
swBindSocket.setChecked(prefs.getBoolean("bind_socket", false));
swStandaloneVpn.setChecked(prefs.getBoolean("standalone_vpn", false));
swTcpKeepAlive.setChecked(prefs.getBoolean("tcp_keep_alive", false));
+ swSslAnchor.setChecked(prefs.getBoolean("ssl_anchor", !BuildConfig.PLAY_STORE_RELEASE));
swSslHarden.setChecked(prefs.getBoolean("ssl_harden", false));
}
diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java
index 4cadb151d3..391b85b266 100644
--- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java
+++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java
@@ -151,7 +151,7 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
"sync_folders",
"sync_shared_folders",
"download_headers", "download_eml",
- "prefer_ip4", "bind_socket", "standalone_vpn", "tcp_keep_alive", "ssl_harden", // force reconnect
+ "prefer_ip4", "bind_socket", "standalone_vpn", "tcp_keep_alive", "ssl_anchor", "ssl_harden", // force reconnect
"experiments", "debug", "protocol", // force reconnect
"auth_plain", "auth_login", "auth_ntlm", "auth_sasl", // force reconnect
"keep_alive_poll", "empty_pool", "idle_done", // force reconnect
diff --git a/app/src/main/res/layout/fragment_options_connection.xml b/app/src/main/res/layout/fragment_options_connection.xml
index 862f10be21..ba61a2347d 100644
--- a/app/src/main/res/layout/fragment_options_connection.xml
+++ b/app/src/main/res/layout/fragment_options_connection.xml
@@ -379,6 +379,29 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swTcpKeepAlive" />
+
+
+
+
Bind sockets to the active network
Standalone VPN
TCP keep alive
+ Anchor SSL connections
Harden SSL connections
Manage connectivity
@@ -755,6 +756,7 @@
Enabling this can cause connection problems on some devices
This can result in not synchronizing messages, for example when using a VPN, but also in other situations
The read/write timeout will be set to the double of the connection timeout. Higher values will result in more battery use.
+ Enabling this will check the root certificate of server certificate chains
Enabling this will disable weak SSL protocols and ciphers, which can lead to connection problems
Messages headers will always be fetched when roaming. You can use the device\'s roaming setting to disable internet while roaming.