Secure fallback

pull/170/head
M66B 5 years ago
parent c678e37bbf
commit 0cefa93c1d

@ -265,11 +265,11 @@ public class MailService implements AutoCloseable {
try {
// Some devices resolve IPv6 addresses while not having IPv6 connectivity
InetAddress[] iaddrs = InetAddress.getAllByName(host);
Log.i("Fallback count=" + iaddrs.length);
if (iaddrs.length > 1)
for (InetAddress iaddr : iaddrs)
try {
Log.i("Falling back to " + iaddr.getHostAddress());
factory.setCheckServer(false);
_connect(context, iaddr.getHostAddress(), port, user, password, factory);
return null;
} catch (MessagingException ex1) {
@ -440,11 +440,19 @@ public class MailService implements AutoCloseable {
private static class SSLSocketFactoryService extends SSLSocketFactory {
// openssl s_client -connect host:port < /dev/null 2>/dev/null | openssl x509 -fingerprint -noout -in /dev/stdin
private SSLContext sslcontext;
private String server;
private boolean secure;
private String trustedFingerprint;
private SSLSocketFactory factory;
private X509Certificate certificate;
private boolean checkServer = true;
SSLSocketFactoryService(final String host, final boolean insecure, final String trustedFingerprint) throws GeneralSecurityException {
SSLSocketFactoryService(String host, boolean insecure, String fingerprint) throws GeneralSecurityException {
this.server = host;
this.secure = !insecure;
this.trustedFingerprint = fingerprint;
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
try {
KeyStore ks = KeyStore.getInstance("AndroidCAStore");
@ -455,100 +463,109 @@ public class MailService implements AutoCloseable {
tmf.init((KeyStore) null);
}
final X509TrustManager rtm = (X509TrustManager) tmf.getTrustManagers()[0];
TrustManager[] tms = tmf.getTrustManagers();
if (tms == null || tms.length == 0 || !(tms[0] instanceof X509TrustManager)) {
Log.e("Missing root trust manager");
sslContext.init(null, tms, null);
} else {
final X509TrustManager rtm = (X509TrustManager) tms[0];
X509TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
if (secure)
rtm.checkClientTrusted(chain, authType);
}
X509TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
if (insecure)
return;
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
certificate = chain[0];
if (secure) {
// Get certificate fingerprint
String fingerprint;
try {
fingerprint = getFingerPrint(certificate);
} catch (Throwable ex) {
throw new CertificateException(ex);
}
rtm.checkClientTrusted(chain, authType);
}
// Check if selected fingerprint
if (fingerprint.equals(trustedFingerprint)) {
Log.i("Trusted selected fingerprint");
return;
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
certificate = chain[0];
// Check certificates
rtm.checkServerTrusted(chain, authType);
if (insecure)
return;
// Check host name
List<String> names = getDnsNames(certificate);
for (String name : names)
if (matches(server, name)) {
Log.i("Trusted server=" + server + " name=" + name);
return;
}
String fingerprint;
try {
fingerprint = getFingerPrint(certificate);
} catch (Throwable ex) {
throw new CertificateException(ex);
String error = server + " not in certificate: " + TextUtils.join(",", names);
Log.e(error);
throw new CertificateException(error);
}
}
if (fingerprint.equals(trustedFingerprint))
return;
if (checkServer) {
rtm.checkServerTrusted(chain, authType);
List<String> names = getDnsNames(certificate);
for (String name : names)
if (matches(host, name))
return;
String error = host + " not in certificate: " + TextUtils.join(",", names);
Log.e(error);
throw new CertificateException(error);
@Override
public X509Certificate[] getAcceptedIssuers() {
return rtm.getAcceptedIssuers();
}
}
};
@Override
public X509Certificate[] getAcceptedIssuers() {
return rtm.getAcceptedIssuers();
}
};
sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[]{tm}, null);
}
sslContext.init(null, new TrustManager[]{tm}, null);
}
void setCheckServer(boolean value) {
checkServer = value;
factory = sslContext.getSocketFactory();
}
@Override
public Socket createSocket() throws IOException {
return sslcontext.getSocketFactory().createSocket();
Log.e("createSocket");
throw new IOException("createSocket");
}
@Override
public Socket createSocket(String host, int port) throws IOException {
return sslcontext.getSocketFactory().createSocket(host, port);
return factory.createSocket(server, port);
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return sslcontext.getSocketFactory().createSocket(s, host, port, autoClose);
return factory.createSocket(s, server, port, autoClose);
}
@Override
public Socket createSocket(InetAddress address, int port) throws IOException {
return sslcontext.getSocketFactory().createSocket(address, port);
Log.e("createSocket(address, port)");
throw new IOException("createSocket");
}
@Override
public Socket createSocket(String host, int port, InetAddress clientAddress, int clientPort) throws IOException {
return sslcontext.getSocketFactory().createSocket(host, port, clientAddress, clientPort);
return factory.createSocket(server, port, clientAddress, clientPort);
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort) throws IOException {
return sslcontext.getSocketFactory().createSocket(address, port, clientAddress, clientPort);
Log.e("createSocket(address, port, clientAddress, clientPort)");
throw new IOException("createSocket");
}
@Override
public String[] getDefaultCipherSuites() {
return sslcontext.getSocketFactory().getDefaultCipherSuites();
return factory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return sslcontext.getSocketFactory().getSupportedCipherSuites();
return factory.getSupportedCipherSuites();
}
private static boolean matches(String server, String name) {

Loading…
Cancel
Save