diff --git a/app/src/dummy/java/eu/faircode/email/SSLHelper.java b/app/src/dummy/java/eu/faircode/email/SSLHelper.java index 7a1cf5bd11..74b2a23be3 100644 --- a/app/src/dummy/java/eu/faircode/email/SSLHelper.java +++ b/app/src/dummy/java/eu/faircode/email/SSLHelper.java @@ -1,16 +1,23 @@ package eu.faircode.email; +import android.text.TextUtils; + import androidx.annotation.NonNull; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.security.Principal; import java.security.cert.CertPathValidatorException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.List; import javax.net.ssl.X509TrustManager; public class SSLHelper { static X509TrustManager getTrustManager(X509TrustManager rtm, + String server, boolean secure, boolean cert_strict, String trustedFingerprint, ITrust intf) { @@ -53,6 +60,41 @@ public class SSLHelper { } else throw new CertificateException(principal.getName(), ex); } + + // Check host name + List names = EntityCertificate.getDnsNames(chain[0]); + if (EntityCertificate.matches(server, names)) + return; + + // Fallback: check server/certificate IP address + if (!cert_strict) + try { + InetAddress ip = InetAddress.getByName(server); + Log.i("Checking server ip=" + ip); + for (String name : names) { + if (name.startsWith("*.")) + name = name.substring(2); + Log.i("Checking cert name=" + name); + + try { + for (InetAddress addr : InetAddress.getAllByName(name)) + if (Arrays.equals(ip.getAddress(), addr.getAddress())) { + Log.i("Accepted " + name + " for " + server); + return; + } + } catch (UnknownHostException ex) { + Log.w(ex); + } + } + } catch (UnknownHostException ex) { + Log.w(ex); + } catch (Throwable ex) { + Log.e(ex); + } + + String error = server + " not in certificate: " + TextUtils.join(",", names); + Log.i(error); + throw new CertificateException(error); } } diff --git a/app/src/extra/java/eu/faircode/email/SSLHelper.java b/app/src/extra/java/eu/faircode/email/SSLHelper.java index 7a1cf5bd11..74b2a23be3 100644 --- a/app/src/extra/java/eu/faircode/email/SSLHelper.java +++ b/app/src/extra/java/eu/faircode/email/SSLHelper.java @@ -1,16 +1,23 @@ package eu.faircode.email; +import android.text.TextUtils; + import androidx.annotation.NonNull; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.security.Principal; import java.security.cert.CertPathValidatorException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.List; import javax.net.ssl.X509TrustManager; public class SSLHelper { static X509TrustManager getTrustManager(X509TrustManager rtm, + String server, boolean secure, boolean cert_strict, String trustedFingerprint, ITrust intf) { @@ -53,6 +60,41 @@ public class SSLHelper { } else throw new CertificateException(principal.getName(), ex); } + + // Check host name + List names = EntityCertificate.getDnsNames(chain[0]); + if (EntityCertificate.matches(server, names)) + return; + + // Fallback: check server/certificate IP address + if (!cert_strict) + try { + InetAddress ip = InetAddress.getByName(server); + Log.i("Checking server ip=" + ip); + for (String name : names) { + if (name.startsWith("*.")) + name = name.substring(2); + Log.i("Checking cert name=" + name); + + try { + for (InetAddress addr : InetAddress.getAllByName(name)) + if (Arrays.equals(ip.getAddress(), addr.getAddress())) { + Log.i("Accepted " + name + " for " + server); + return; + } + } catch (UnknownHostException ex) { + Log.w(ex); + } + } + } catch (UnknownHostException ex) { + Log.w(ex); + } catch (Throwable ex) { + Log.e(ex); + } + + String error = server + " not in certificate: " + TextUtils.join(",", names); + Log.i(error); + throw new CertificateException(error); } } diff --git a/app/src/main/java/eu/faircode/email/EmailService.java b/app/src/main/java/eu/faircode/email/EmailService.java index 4b703edc7d..dfe73b5b3f 100644 --- a/app/src/main/java/eu/faircode/email/EmailService.java +++ b/app/src/main/java/eu/faircode/email/EmailService.java @@ -62,7 +62,6 @@ import java.net.UnknownHostException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.PrivateKey; -import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -90,7 +89,6 @@ import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; @@ -1076,7 +1074,7 @@ public class EmailService implements AutoCloseable { for (TrustManager tm : tms) Log.e("Trust manager " + tm.getClass()); - X509TrustManager tm = SSLHelper.getTrustManager(rtm, secure, cert_strict, trustedFingerprint, + X509TrustManager tm = SSLHelper.getTrustManager(rtm, server, secure, cert_strict, trustedFingerprint, new SSLHelper.ITrust() { @Override public void checkServerTrusted(X509Certificate[] chain) { @@ -1123,62 +1121,7 @@ public class EmailService implements AutoCloseable { public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { configureSocketOptions(s); ApplicationSecure.waitProviderInstalled(); - Socket socket = configure(factory.createSocket(s, server, port, autoClose)); - - if (!secure || !(socket instanceof SSLSocket)) - return socket; - - List names = new ArrayList<>(); - SSLSession session = ((SSLSocket) socket).getSession(); - Certificate[] certs = (session == null ? null : session.getPeerCertificates()); - if (certs != null && certs.length > 0 && certs[0] instanceof X509Certificate) - try { - names = EntityCertificate.getDnsNames((X509Certificate) certs[0]); - } catch (Throwable ex) { - Log.w(ex); - } - - Log.i("SSL session" + - " server=" + server + - " host=" + host + ":" + port + - " host=" + session.getPeerHost() + - " protocol=" + session.getProtocol() + - " cipher=" + session.getCipherSuite() + - " names=" + TextUtils.join(", ", names)); - - // Check host name - if (EntityCertificate.matches(server, names)) - return socket; - - // Fallback: check server/certificate IP address - if (!cert_strict) - try { - InetAddress ip = InetAddress.getByName(server); - Log.i("Checking server ip=" + ip); - for (String name : names) { - if (name.startsWith("*.")) - name = name.substring(2); - Log.i("Checking cert name=" + name); - - try { - for (InetAddress addr : InetAddress.getAllByName(name)) - if (Arrays.equals(ip.getAddress(), addr.getAddress())) { - Log.i("Accepted " + name + " for " + server); - return socket; - } - } catch (UnknownHostException ex) { - Log.w(ex); - } - } - } catch (UnknownHostException ex) { - Log.w(ex); - } catch (Throwable ex) { - Log.e(ex); - } - - String error = server + " not in certificate: " + TextUtils.join(",", names); - Log.i(error); - throw new ConnectException(error); + return configure(factory.createSocket(s, server, port, autoClose)); } @Override