add require channel binding

This commit is contained in:
Daniel Gultsch 2024-04-08 17:20:39 +02:00
parent 7bdd924dd8
commit 32de58ce65
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
6 changed files with 41 additions and 10 deletions

View file

@ -119,4 +119,8 @@ public class AppSettings {
PreferenceManager.getDefaultSharedPreferences(context); PreferenceManager.getDefaultSharedPreferences(context);
sharedPreferences.edit().putBoolean(SEND_CRASH_REPORTS, value).apply(); sharedPreferences.edit().putBoolean(SEND_CRASH_REPORTS, value).apply();
} }
public boolean isRequireChannelBinding() {
return getBooleanPreference(REQUIRE_CHANNEL_BINDING, R.bool.require_channel_binding);
}
} }

View file

@ -6,17 +6,17 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import java.util.Collection;
import java.util.Collections;
import javax.net.ssl.SSLSocket;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.SSLSockets; import eu.siacs.conversations.utils.SSLSockets;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xml.Namespace;
import java.util.Collection;
import java.util.Collections;
import javax.net.ssl.SSLSocket;
public abstract class SaslMechanism { public abstract class SaslMechanism {
protected final Account account; protected final Account account;
@ -170,7 +170,9 @@ public abstract class SaslMechanism {
} }
public static SaslMechanism ensureAvailable( public static SaslMechanism ensureAvailable(
final SaslMechanism mechanism, final SSLSockets.Version sslVersion) { final SaslMechanism mechanism,
final SSLSockets.Version sslVersion,
final boolean requireChannelBinding) {
if (mechanism instanceof ChannelBindingMechanism) { if (mechanism instanceof ChannelBindingMechanism) {
final ChannelBinding cb = ((ChannelBindingMechanism) mechanism).getChannelBinding(); final ChannelBinding cb = ((ChannelBindingMechanism) mechanism).getChannelBinding();
if (ChannelBinding.isAvailable(cb, sslVersion)) { if (ChannelBinding.isAvailable(cb, sslVersion)) {
@ -181,6 +183,9 @@ public abstract class SaslMechanism {
"pinned channel binding method " + cb + " no longer available"); "pinned channel binding method " + cb + " no longer available");
return null; return null;
} }
} else if (requireChannelBinding) {
Log.d(Config.LOGTAG, "pinned mechanism did not provide channel binding");
return null;
} else { } else {
return mechanism; return mechanism;
} }

View file

@ -70,7 +70,9 @@ public class SecuritySettingsFragment extends XmppPreferenceFragment {
requireService().updateMemorizingTrustManager(); requireService().updateMemorizingTrustManager();
reconnectAccounts(); reconnectAccounts();
} }
case AppSettings.REQUIRE_CHANNEL_BINDING -> {} case AppSettings.REQUIRE_CHANNEL_BINDING -> {
reconnectAccounts();
}
} }
} }

View file

@ -22,6 +22,7 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.XmppDomainVerifier; import eu.siacs.conversations.crypto.XmppDomainVerifier;
@ -157,6 +158,7 @@ public class XmppConnection implements Runnable {
new Hashtable<>(); new Hashtable<>();
private final Set<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = private final Set<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners =
new HashSet<>(); new HashSet<>();
private final AppSettings appSettings;
private final XmppConnectionService mXmppConnectionService; private final XmppConnectionService mXmppConnectionService;
private Socket socket; private Socket socket;
private XmlReader tagReader; private XmlReader tagReader;
@ -203,9 +205,10 @@ public class XmppConnection implements Runnable {
public XmppConnection(final Account account, final XmppConnectionService service) { public XmppConnection(final Account account, final XmppConnectionService service) {
this.account = account; this.account = account;
this.mXmppConnectionService = service; this.mXmppConnectionService = service;
this.appSettings = new AppSettings(mXmppConnectionService.getApplicationContext());
} }
private static void fixResource(Context context, Account account) { private static void fixResource(final Context context, final Account account) {
String resource = account.getResource(); String resource = account.getResource();
int fixedPartLength = int fixedPartLength =
context.getString(R.string.app_name).length() + 1; // include the trailing dot context.getString(R.string.app_name).length() + 1; // include the trailing dot
@ -1646,6 +1649,7 @@ public class XmppConnection implements Runnable {
+ mechanisms); + mechanisms);
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER); throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
} }
validateRequireChannelBinding(saslMechanism);
if (SaslMechanism.hashedToken(saslMechanism)) { if (SaslMechanism.hashedToken(saslMechanism)) {
return; return;
} }
@ -1664,6 +1668,17 @@ public class XmppConnection implements Runnable {
} }
} }
private void validateRequireChannelBinding(@NonNull final SaslMechanism mechanism)
throws StateChangingException {
if (appSettings.isRequireChannelBinding()) {
if (mechanism instanceof ChannelBindingMechanism) {
return;
}
Log.d(Config.LOGTAG, account.getJid() + ": server did not offer channel binding");
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
}
}
private Element generateAuthenticationRequest( private Element generateAuthenticationRequest(
final String firstMessage, final boolean usingFast) { final String firstMessage, final boolean usingFast) {
return generateAuthenticationRequest( return generateAuthenticationRequest(
@ -2365,7 +2380,10 @@ public class XmppConnection implements Runnable {
final SaslMechanism quickStartMechanism; final SaslMechanism quickStartMechanism;
if (secureConnection) { if (secureConnection) {
quickStartMechanism = quickStartMechanism =
SaslMechanism.ensureAvailable(account.getQuickStartMechanism(), sslVersion); SaslMechanism.ensureAvailable(
account.getQuickStartMechanism(),
sslVersion,
appSettings.isRequireChannelBinding());
} else { } else {
quickStartMechanism = null; quickStartMechanism = null;
} }

View file

@ -44,4 +44,5 @@
<bool name="allow_screenshots">true</bool> <bool name="allow_screenshots">true</bool>
<string name="default_push_server">up.conversations.im</string> <string name="default_push_server">up.conversations.im</string>
<string name="default_push_account">none</string> <string name="default_push_account">none</string>
<bool name="require_channel_binding">false</bool>
</resources> </resources>

View file

@ -30,6 +30,7 @@
android:title="@string/pref_remove_trusted_certificates_title" /> android:title="@string/pref_remove_trusted_certificates_title" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:defaultValue="@bool/require_channel_binding"
android:icon="@drawable/ic_private_connectivity_24dp" android:icon="@drawable/ic_private_connectivity_24dp"
android:key="channel_binding_required" android:key="channel_binding_required"
android:summary="@string/detect_mim_summary" android:summary="@string/detect_mim_summary"
@ -37,8 +38,8 @@
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/pref_category_on_this_device"> <PreferenceCategory android:title="@string/pref_category_on_this_device">
<ListPreference <ListPreference
android:icon="@drawable/ic_auto_delete_24dp"
android:defaultValue="@integer/automatic_message_deletion" android:defaultValue="@integer/automatic_message_deletion"
android:icon="@drawable/ic_auto_delete_24dp"
android:key="automatic_message_deletion" android:key="automatic_message_deletion"
android:summary="@string/pref_automatically_delete_messages_description" android:summary="@string/pref_automatically_delete_messages_description"
android:title="@string/pref_automatically_delete_messages" /> android:title="@string/pref_automatically_delete_messages" />