mirror of
https://github.com/M66B/FairEmail.git
synced 2026-03-31 14:17:03 +02:00
Added option to harden SSL connections
This commit is contained in:
@@ -56,6 +56,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
|
||||
private Spinner spDownload;
|
||||
private SwitchCompat swRoaming;
|
||||
private SwitchCompat swRlah;
|
||||
private SwitchCompat swSslHarden;
|
||||
private SwitchCompat swSocks;
|
||||
private EditText etSocks;
|
||||
private Button btnSocks;
|
||||
@@ -64,7 +65,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
|
||||
private TextView tvConnectionRoaming;
|
||||
|
||||
private final static String[] RESET_OPTIONS = new String[]{
|
||||
"metered", "download", "roaming", "rlah", "socks_enabled", "socks_proxy"
|
||||
"metered", "download", "roaming", "rlah", "ssl_harden", "socks_enabled", "socks_proxy"
|
||||
};
|
||||
|
||||
@Override
|
||||
@@ -81,6 +82,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
|
||||
spDownload = view.findViewById(R.id.spDownload);
|
||||
swRoaming = view.findViewById(R.id.swRoaming);
|
||||
swRlah = view.findViewById(R.id.swRlah);
|
||||
swSslHarden = view.findViewById(R.id.swSslHarden);
|
||||
swSocks = view.findViewById(R.id.swSocks);
|
||||
etSocks = view.findViewById(R.id.etSocks);
|
||||
btnSocks = view.findViewById(R.id.btnSocks);
|
||||
@@ -129,6 +131,13 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
|
||||
}
|
||||
});
|
||||
|
||||
swSslHarden.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
|
||||
prefs.edit().putBoolean("ssl_harden", checked).apply();
|
||||
}
|
||||
});
|
||||
|
||||
swSocks.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
|
||||
@@ -246,6 +255,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
|
||||
|
||||
swRoaming.setChecked(prefs.getBoolean("roaming", true));
|
||||
swRlah.setChecked(prefs.getBoolean("rlah", true));
|
||||
swSslHarden.setChecked(prefs.getBoolean("ssl_harden", false));
|
||||
swSocks.setChecked(prefs.getBoolean("socks_enabled", false));
|
||||
etSocks.setText(prefs.getString("socks_proxy", null));
|
||||
etSocks.setEnabled(swSocks.isChecked());
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -49,6 +48,7 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.mail.AuthenticationFailedException;
|
||||
import javax.mail.Folder;
|
||||
@@ -58,9 +58,7 @@ import javax.mail.Service;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.Store;
|
||||
import javax.mail.event.StoreListener;
|
||||
import javax.net.ssl.SNIHostName;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
@@ -71,6 +69,7 @@ public class MailService implements AutoCloseable {
|
||||
private Context context;
|
||||
private String protocol;
|
||||
private boolean insecure;
|
||||
private boolean harden;
|
||||
private boolean useip;
|
||||
private boolean debug;
|
||||
private Properties properties;
|
||||
@@ -93,7 +92,12 @@ public class MailService implements AutoCloseable {
|
||||
|
||||
private static final int APPEND_BUFFER_SIZE = 4 * 1024 * 1024; // bytes
|
||||
|
||||
private static final List<String> SSL_PROTOCOL_BLACKLIST = Collections.unmodifiableList(Arrays.asList(
|
||||
"SSLv2", "SSLv3", "TLSv1", "TLSv1.1"
|
||||
));
|
||||
|
||||
private MailService() {
|
||||
// Prevent instantiation
|
||||
}
|
||||
|
||||
MailService(Context context, String protocol, String realm, boolean insecure, boolean check, boolean debug) throws NoSuchProviderException {
|
||||
@@ -105,6 +109,8 @@ public class MailService implements AutoCloseable {
|
||||
properties = MessageHelper.getSessionProperties();
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
this.harden = prefs.getBoolean("ssl_harden", false);
|
||||
|
||||
boolean socks_enabled = prefs.getBoolean("socks_enabled", false);
|
||||
String socks_proxy = prefs.getString("socks_proxy", "localhost:9050");
|
||||
|
||||
@@ -230,7 +236,7 @@ public class MailService implements AutoCloseable {
|
||||
String fingerprint) throws MessagingException {
|
||||
SSLSocketFactoryService factory = null;
|
||||
try {
|
||||
factory = new SSLSocketFactoryService(host, insecure, fingerprint);
|
||||
factory = new SSLSocketFactoryService(host, insecure, harden, fingerprint);
|
||||
properties.put("mail." + protocol + ".ssl.socketFactory", factory);
|
||||
properties.put("mail." + protocol + ".socketFactory.fallback", "false");
|
||||
properties.put("mail." + protocol + ".ssl.checkserveridentity", "false");
|
||||
@@ -533,13 +539,15 @@ public class MailService 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 harden;
|
||||
private String trustedFingerprint;
|
||||
private SSLSocketFactory factory;
|
||||
private X509Certificate certificate;
|
||||
|
||||
SSLSocketFactoryService(String host, boolean insecure, String fingerprint) throws GeneralSecurityException {
|
||||
SSLSocketFactoryService(String host, boolean insecure, boolean harden, String fingerprint) throws GeneralSecurityException {
|
||||
this.server = host;
|
||||
this.secure = !insecure;
|
||||
this.harden = harden;
|
||||
this.trustedFingerprint = fingerprint;
|
||||
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
@@ -651,19 +659,31 @@ public class MailService implements AutoCloseable {
|
||||
}
|
||||
|
||||
private Socket configure(Socket socket) {
|
||||
/*
|
||||
if (socket instanceof SSLSocket
|
||||
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
SSLParameters params = ((SSLSocket) socket).getSSLParameters();
|
||||
if (params == null)
|
||||
params = new SSLParameters();
|
||||
Log.i("SNI server names=" + params.getServerNames());
|
||||
params.setEndpointIdentificationAlgorithm("HTTPS");
|
||||
params.setServerNames(Arrays.asList(new SNIHostName(server)));
|
||||
Log.i("SNI server name=" + params.getServerNames().get(0));
|
||||
((SSLSocket) socket).setSSLParameters(params);
|
||||
if (harden && socket instanceof SSLSocket) {
|
||||
// https://developer.android.com/reference/javax/net/ssl/SSLSocket.html
|
||||
SSLSocket sslSocket = (SSLSocket) socket;
|
||||
|
||||
List<String> protocols = new ArrayList<>();
|
||||
for (String protocol : sslSocket.getEnabledProtocols())
|
||||
if (SSL_PROTOCOL_BLACKLIST.contains(protocol))
|
||||
Log.i("SSL disabling protocol=" + protocol);
|
||||
else
|
||||
protocols.add(protocol);
|
||||
Log.i("SSL protocols=" + TextUtils.join(",", protocols));
|
||||
sslSocket.setEnabledProtocols(protocols.toArray(new String[0]));
|
||||
|
||||
ArrayList<String> ciphers = new ArrayList<>();
|
||||
Pattern pattern = Pattern.compile(".*(_DES|DH_|DSS|EXPORT|MD5|NULL|RC4|TLS_FALLBACK_SCSV).*");
|
||||
for (String cipher : sslSocket.getEnabledCipherSuites()) {
|
||||
if (pattern.matcher(cipher).matches())
|
||||
Log.i("SSL disabling cipher=" + cipher);
|
||||
else
|
||||
ciphers.add(cipher);
|
||||
}
|
||||
Log.i("SSL ciphers=" + TextUtils.join(",", ciphers));
|
||||
sslSocket.setEnabledCipherSuites(ciphers.toArray(new String[0]));
|
||||
}
|
||||
*/
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
|
||||
|
||||
private static final List<String> PREF_RELOAD = Collections.unmodifiableList(Arrays.asList(
|
||||
"metered", "roaming", "rlah", // force reconnect
|
||||
"ssl_harden", // force reconnect
|
||||
"socks_enabled", "socks_proxy", // force reconnect
|
||||
"subscribed_only", // force folder sync
|
||||
"badge", "unseen_ignored", // force update badge/widget
|
||||
|
||||
@@ -119,6 +119,30 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/swRlah" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/swSslHarden"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:checked="true"
|
||||
android:text="@string/title_advanced_ssl_harden"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvRlahHint"
|
||||
app:switchPadding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSslHardenHint"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="48dp"
|
||||
android:text="@string/title_advanced_ssl_harden_hint"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:textStyle="italic"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/swSslHarden" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/swSocks"
|
||||
android:layout_width="0dp"
|
||||
@@ -128,7 +152,7 @@
|
||||
android:text="@string/title_advanced_socks"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvRlahHint"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvSslHardenHint"
|
||||
app:switchPadding="12dp" />
|
||||
|
||||
<EditText
|
||||
|
||||
@@ -279,6 +279,7 @@
|
||||
<string name="title_advanced_download">Automatically download messages and attachments on a metered connection up to</string>
|
||||
<string name="title_advanced_roaming">Download messages and attachments while roaming</string>
|
||||
<string name="title_advanced_rlah">Roam like at home</string>
|
||||
<string name="title_advanced_ssl_harden">Harden SSL connections</string>
|
||||
<string name="title_advanced_socks">Use SOCKS proxy</string>
|
||||
<string name="title_advanced_manage_connectivity">Manage connectivity</string>
|
||||
|
||||
@@ -423,6 +424,7 @@
|
||||
<string name="title_advanced_metered_hint">Metered connections are generally mobile connections or paid Wi-Fi hotspots</string>
|
||||
<string name="title_advanced_metered_warning">Disabling this option will disable receiving and sending messages on mobile internet connections</string>
|
||||
<string name="title_advanced_rlah_hint">Assuming no roaming within the EU</string>
|
||||
<string name="title_advanced_ssl_harden_hint">Enabling this will disable weak SSL protocols and ciphers</string>
|
||||
<string name="title_advanced_socks_hint">Using a remote proxy server is insecure because proxy connections are not encrypted</string>
|
||||
<string name="title_advanced_roaming_hint">Messages headers will always be fetched when roaming. You can use the device\'s roaming setting to disable internet while roaming.</string>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user