simplify loginInfo null check

This commit is contained in:
Daniel Gultsch 2024-02-04 19:49:55 +01:00
parent a6ba658494
commit 5ab8912cb4
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2

View file

@ -22,47 +22,6 @@ 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 org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.IDN;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
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;
@ -110,8 +69,50 @@ import eu.siacs.conversations.xmpp.stanzas.streammgmt.AckPacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.EnablePacket; import eu.siacs.conversations.xmpp.stanzas.streammgmt.EnablePacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket; import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket; import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.IDN;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
public class XmppConnection implements Runnable { public class XmppConnection implements Runnable {
private static final int PACKET_IQ = 0; private static final int PACKET_IQ = 0;
@ -283,12 +284,14 @@ public class XmppConnection implements Runnable {
mXmppConnectionService.resetSendingToWaiting(account); mXmppConnectionService.resetSendingToWaiting(account);
} }
Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": connecting"); Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": connecting");
features.encryptionEnabled = false; this.loginInfo = null;
this.features.encryptionEnabled = false;
this.inSmacksSession = false; this.inSmacksSession = false;
this.quickStartInProgress = false; this.quickStartInProgress = false;
this.isBound = false; this.isBound = false;
this.attempt++; this.attempt++;
this.verifiedHostname = null; // will be set if user entered hostname is being used or hostname was verified this.verifiedHostname =
null; // will be set if user entered hostname is being used or hostname was verified
// with dnssec // with dnssec
try { try {
Socket localSocket; Socket localSocket;
@ -370,12 +373,18 @@ public class XmppConnection implements Runnable {
final StreamId streamId = this.streamId; final StreamId streamId = this.streamId;
final Resolver.Result resumeLocation = streamId == null ? null : streamId.location; final Resolver.Result resumeLocation = streamId == null ? null : streamId.location;
if (resumeLocation != null) { if (resumeLocation != null) {
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": injected resume location on position 0"); Log.d(
Config.LOGTAG,
account.getJid().asBareJid()
+ ": injected resume location on position 0");
results.add(0, resumeLocation); results.add(0, resumeLocation);
} }
final Resolver.Result seeOtherHost = this.seeOtherHostResolverResult; final Resolver.Result seeOtherHost = this.seeOtherHostResolverResult;
if (seeOtherHost != null) { if (seeOtherHost != null) {
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": injected see-other-host on position 0"); Log.d(
Config.LOGTAG,
account.getJid().asBareJid()
+ ": injected see-other-host on position 0");
results.add(0, seeOtherHost); results.add(0, seeOtherHost);
} }
for (final Iterator<Resolver.Result> iterator = results.iterator(); for (final Iterator<Resolver.Result> iterator = results.iterator();
@ -585,13 +594,18 @@ public class XmppConnection implements Runnable {
processStreamFeatures(nextTag); processStreamFeatures(nextTag);
} else if (nextTag.isStart("proceed", Namespace.TLS)) { } else if (nextTag.isStart("proceed", Namespace.TLS)) {
switchOverToTls(); switchOverToTls();
} else if (nextTag.isStart("failure", Namespace.TLS)) {
throw new StateChangingException(Account.State.TLS_ERROR);
} else if (account.isOptionSet(Account.OPTION_REGISTER)
&& nextTag.isStart("iq", Namespace.JABBER_CLIENT)) {
processIq(nextTag);
} else if (!isSecure() || this.loginInfo == null) {
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
} else if (nextTag.isStart("success")) { } else if (nextTag.isStart("success")) {
final Element success = tagReader.readElement(nextTag); final Element success = tagReader.readElement(nextTag);
if (processSuccess(success)) { if (processSuccess(success)) {
break; break;
} }
} else if (nextTag.isStart("failure", Namespace.TLS)) {
throw new StateChangingException(Account.State.TLS_ERROR);
} else if (nextTag.isStart("failure")) { } else if (nextTag.isStart("failure")) {
final Element failure = tagReader.readElement(nextTag); final Element failure = tagReader.readElement(nextTag);
processFailure(failure); processFailure(failure);
@ -599,16 +613,8 @@ public class XmppConnection implements Runnable {
// two step sasl2 - we dont support this yet // two step sasl2 - we dont support this yet
throw new StateChangingException(Account.State.INCOMPATIBLE_CLIENT); throw new StateChangingException(Account.State.INCOMPATIBLE_CLIENT);
} else if (nextTag.isStart("challenge")) { } else if (nextTag.isStart("challenge")) {
if (isSecure() && this.loginInfo != null) {
final Element challenge = tagReader.readElement(nextTag); final Element challenge = tagReader.readElement(nextTag);
processChallenge(challenge); processChallenge(challenge);
} else {
Log.d(
Config.LOGTAG,
account.getJid().asBareJid()
+ ": received 'challenge on an unsecure connection");
throw new StateChangingException(Account.State.INCOMPATIBLE_CLIENT);
}
} else if (nextTag.isStart("enabled", Namespace.STREAM_MANAGEMENT)) { } else if (nextTag.isStart("enabled", Namespace.STREAM_MANAGEMENT)) {
final Element enabled = tagReader.readElement(nextTag); final Element enabled = tagReader.readElement(nextTag);
processEnabled(enabled); processEnabled(enabled);
@ -674,6 +680,8 @@ public class XmppConnection implements Runnable {
processFailed(failed, true); processFailed(failed, true);
} else if (nextTag.isStart("iq", Namespace.JABBER_CLIENT)) { } else if (nextTag.isStart("iq", Namespace.JABBER_CLIENT)) {
processIq(nextTag); processIq(nextTag);
} else if (!isBound) {
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
} else if (nextTag.isStart("message", Namespace.JABBER_CLIENT)) { } else if (nextTag.isStart("message", Namespace.JABBER_CLIENT)) {
processMessage(nextTag); processMessage(nextTag);
} else if (nextTag.isStart("presence", Namespace.JABBER_CLIENT)) { } else if (nextTag.isStart("presence", Namespace.JABBER_CLIENT)) {
@ -709,7 +717,9 @@ public class XmppConnection implements Runnable {
throw new AssertionError("Missing implementation for " + version); throw new AssertionError("Missing implementation for " + version);
} }
try { try {
response.setContent(this.loginInfo.saslMechanism.getResponse(challenge.getContent(), sslSocketOrNull(socket))); response.setContent(
this.loginInfo.saslMechanism.getResponse(
challenge.getContent(), sslSocketOrNull(socket)));
} catch (final SaslMechanism.AuthenticationException e) { } catch (final SaslMechanism.AuthenticationException e) {
// TODO: Send auth abort tag. // TODO: Send auth abort tag.
Log.e(Config.LOGTAG, e.toString()); Log.e(Config.LOGTAG, e.toString());
@ -804,7 +814,10 @@ public class XmppConnection implements Runnable {
if (resumed != null && streamId != null) { if (resumed != null && streamId != null) {
if (this.boundStreamFeatures != null) { if (this.boundStreamFeatures != null) {
this.streamFeatures = this.boundStreamFeatures; this.streamFeatures = this.boundStreamFeatures;
Log.d(Config.LOGTAG, "putting previous stream features back in place: " + XmlHelper.printElementNames(this.boundStreamFeatures)); Log.d(
Config.LOGTAG,
"putting previous stream features back in place: "
+ XmlHelper.printElementNames(this.boundStreamFeatures));
} }
processResumed(resumed); processResumed(resumed);
} else if (failed != null) { } else if (failed != null) {
@ -856,13 +869,22 @@ public class XmppConnection implements Runnable {
tokenMechanism = null; tokenMechanism = null;
} }
if (tokenMechanism != null && !Strings.isNullOrEmpty(token)) { if (tokenMechanism != null && !Strings.isNullOrEmpty(token)) {
if (ChannelBinding.priority(tokenMechanism.channelBinding) >= ChannelBindingMechanism.getPriority(currentSaslMechanism)) { if (ChannelBinding.priority(tokenMechanism.channelBinding)
>= ChannelBindingMechanism.getPriority(currentSaslMechanism)) {
this.account.setFastToken(tokenMechanism, token); this.account.setFastToken(tokenMechanism, token);
Log.d( Log.d(
Config.LOGTAG, Config.LOGTAG,
account.getJid().asBareJid() + ": storing hashed token " + tokenMechanism); account.getJid().asBareJid()
+ ": storing hashed token "
+ tokenMechanism);
} else { } else {
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": not accepting hashed token "+ tokenMechanism.name()+" for log in mechanism "+currentSaslMechanism.getMechanism()); Log.d(
Config.LOGTAG,
account.getJid().asBareJid()
+ ": not accepting hashed token "
+ tokenMechanism.name()
+ " for log in mechanism "
+ currentSaslMechanism.getMechanism());
this.account.resetFastToken(); this.account.resetFastToken();
} }
} else if (this.hashTokenRequest != null) { } else if (this.hashTokenRequest != null) {
@ -1015,7 +1037,9 @@ public class XmppConnection implements Runnable {
} else { } else {
Log.d( Log.d(
Config.LOGTAG, Config.LOGTAG,
account.getJid().asBareJid() + ": stream management enabled. resume at: " + streamId.location); account.getJid().asBareJid()
+ ": stream management enabled. resume at: "
+ streamId.location);
} }
this.streamId = streamId; this.streamId = streamId;
this.stanzasReceived = 0; this.stanzasReceived = 0;
@ -1061,8 +1085,7 @@ public class XmppConnection implements Runnable {
Config.LOGTAG, Config.LOGTAG,
account.getJid().asBareJid() + ": resending " + failedStanzas.size() + " stanzas"); account.getJid().asBareJid() + ": resending " + failedStanzas.size() + " stanzas");
for (final AbstractAcknowledgeableStanza packet : failedStanzas) { for (final AbstractAcknowledgeableStanza packet : failedStanzas) {
if (packet instanceof MessagePacket) { if (packet instanceof MessagePacket message) {
MessagePacket message = (MessagePacket) packet;
mXmppConnectionService.markMessage( mXmppConnectionService.markMessage(
account, account,
message.getTo().asBareJid(), message.getTo().asBareJid(),
@ -1143,20 +1166,13 @@ public class XmppConnection implements Runnable {
private @NonNull Element processPacket(final Tag currentTag, final int packetType) private @NonNull Element processPacket(final Tag currentTag, final int packetType)
throws IOException { throws IOException {
final Element element; final Element element =
switch (packetType) { switch (packetType) {
case PACKET_IQ: case PACKET_IQ -> new IqPacket();
element = new IqPacket(); case PACKET_MESSAGE -> new MessagePacket();
break; case PACKET_PRESENCE -> new PresencePacket();
case PACKET_MESSAGE: default -> throw new AssertionError("Should never encounter invalid type");
element = new MessagePacket(); };
break;
case PACKET_PRESENCE:
element = new PresencePacket();
break;
default:
throw new AssertionError("Should never encounter invalid type");
}
element.setAttributes(currentTag.getAttributes()); element.setAttributes(currentTag.getAttributes());
Tag nextTag = tagReader.readTag(); Tag nextTag = tagReader.readTag();
if (nextTag == null) { if (nextTag == null) {
@ -1476,10 +1492,12 @@ public class XmppConnection implements Runnable {
this.streamFeatures.findChild("sasl-channel-binding", Namespace.CHANNEL_BINDING); this.streamFeatures.findChild("sasl-channel-binding", Namespace.CHANNEL_BINDING);
final Collection<ChannelBinding> channelBindings = ChannelBinding.of(cbElement); final Collection<ChannelBinding> channelBindings = ChannelBinding.of(cbElement);
final SaslMechanism.Factory factory = new SaslMechanism.Factory(account); final SaslMechanism.Factory factory = new SaslMechanism.Factory(account);
final SaslMechanism saslMechanism = factory.of(mechanisms, channelBindings, version, SSLSockets.version(this.socket)); final SaslMechanism saslMechanism =
factory.of(mechanisms, channelBindings, version, SSLSockets.version(this.socket));
this.validate(saslMechanism, mechanisms); this.validate(saslMechanism, mechanisms);
final boolean quickStartAvailable; final boolean quickStartAvailable;
final String firstMessage = saslMechanism.getClientFirstMessage(sslSocketOrNull(this.socket)); final String firstMessage =
saslMechanism.getClientFirstMessage(sslSocketOrNull(this.socket));
final boolean usingFast = SaslMechanism.hashedToken(saslMechanism); final boolean usingFast = SaslMechanism.hashedToken(saslMechanism);
final Element authenticate; final Element authenticate;
if (version == SaslMechanism.Version.SASL) { if (version == SaslMechanism.Version.SASL) {
@ -1496,7 +1514,8 @@ public class XmppConnection implements Runnable {
if (usingFast) { if (usingFast) {
hashTokenRequest = null; hashTokenRequest = null;
} else { } else {
final Element fast = inline == null ? null : inline.findChild("fast", Namespace.FAST); final Element fast =
inline == null ? null : inline.findChild("fast", Namespace.FAST);
final Collection<String> fastMechanisms = SaslMechanism.mechanisms(fast); final Collection<String> fastMechanisms = SaslMechanism.mechanisms(fast);
hashTokenRequest = hashTokenRequest =
HashedToken.Mechanism.best(fastMechanisms, SSLSockets.version(this.socket)); HashedToken.Mechanism.best(fastMechanisms, SSLSockets.version(this.socket));
@ -1519,7 +1538,9 @@ public class XmppConnection implements Runnable {
} }
this.loginInfo = new LoginInfo(saslMechanism, version, bindFeatures); this.loginInfo = new LoginInfo(saslMechanism, version, bindFeatures);
this.hashTokenRequest = hashTokenRequest; this.hashTokenRequest = hashTokenRequest;
authenticate = generateAuthenticationRequest(firstMessage, usingFast, hashTokenRequest, bindFeatures, sm); authenticate =
generateAuthenticationRequest(
firstMessage, usingFast, hashTokenRequest, bindFeatures, sm);
} else { } else {
throw new AssertionError("Missing implementation for " + version); throw new AssertionError("Missing implementation for " + version);
} }
@ -1547,7 +1568,9 @@ public class XmppConnection implements Runnable {
return inline != null && inline.hasChild("fast", Namespace.FAST); return inline != null && inline.hasChild("fast", Namespace.FAST);
} }
private void validate(final @Nullable SaslMechanism saslMechanism, Collection<String> mechanisms) throws StateChangingException { private void validate(
final @Nullable SaslMechanism saslMechanism, Collection<String> mechanisms)
throws StateChangingException {
if (saslMechanism == null) { if (saslMechanism == null) {
Log.d( Log.d(
Config.LOGTAG, Config.LOGTAG,
@ -1574,8 +1597,10 @@ public class XmppConnection implements Runnable {
} }
} }
private Element generateAuthenticationRequest(final String firstMessage, final boolean usingFast) { private Element generateAuthenticationRequest(
return generateAuthenticationRequest(firstMessage, usingFast, null, Bind2.QUICKSTART_FEATURES, true); final String firstMessage, final boolean usingFast) {
return generateAuthenticationRequest(
firstMessage, usingFast, null, Bind2.QUICKSTART_FEATURES, true);
} }
private Element generateAuthenticationRequest( private Element generateAuthenticationRequest(
@ -2225,10 +2250,18 @@ public class XmppConnection implements Runnable {
final String seeOtherHost = streamError.findChildContent("see-other-host"); final String seeOtherHost = streamError.findChildContent("see-other-host");
final Resolver.Result currentResolverResult = this.currentResolverResult; final Resolver.Result currentResolverResult = this.currentResolverResult;
if (Strings.isNullOrEmpty(seeOtherHost) || currentResolverResult == null) { if (Strings.isNullOrEmpty(seeOtherHost) || currentResolverResult == null) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": stream error " + streamError); Log.d(
Config.LOGTAG,
account.getJid().asBareJid() + ": stream error " + streamError);
throw new StateChangingException(Account.State.STREAM_ERROR); throw new StateChangingException(Account.State.STREAM_ERROR);
} }
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": see other host: "+seeOtherHost+" "+currentResolverResult); Log.d(
Config.LOGTAG,
account.getJid().asBareJid()
+ ": see other host: "
+ seeOtherHost
+ " "
+ currentResolverResult);
final Resolver.Result seeOtherResult = currentResolverResult.seeOtherHost(seeOtherHost); final Resolver.Result seeOtherResult = currentResolverResult.seeOtherHost(seeOtherHost);
if (seeOtherResult != null) { if (seeOtherResult != null) {
this.seeOtherHostResolverResult = seeOtherResult; this.seeOtherHostResolverResult = seeOtherResult;
@ -2262,7 +2295,8 @@ public class XmppConnection implements Runnable {
final boolean secureConnection = sslVersion != SSLSockets.Version.NONE; final boolean secureConnection = sslVersion != SSLSockets.Version.NONE;
final SaslMechanism quickStartMechanism; final SaslMechanism quickStartMechanism;
if (secureConnection) { if (secureConnection) {
quickStartMechanism = SaslMechanism.ensureAvailable(account.getQuickStartMechanism(), sslVersion); quickStartMechanism =
SaslMechanism.ensureAvailable(account.getQuickStartMechanism(), sslVersion);
} else { } else {
quickStartMechanism = null; quickStartMechanism = null;
} }
@ -2271,10 +2305,16 @@ public class XmppConnection implements Runnable {
&& quickStartMechanism != null && quickStartMechanism != null
&& account.isOptionSet(Account.OPTION_QUICKSTART_AVAILABLE)) { && account.isOptionSet(Account.OPTION_QUICKSTART_AVAILABLE)) {
mXmppConnectionService.restoredFromDatabaseLatch.await(); mXmppConnectionService.restoredFromDatabaseLatch.await();
this.loginInfo = new LoginInfo(quickStartMechanism, SaslMechanism.Version.SASL_2, Bind2.QUICKSTART_FEATURES); this.loginInfo =
new LoginInfo(
quickStartMechanism,
SaslMechanism.Version.SASL_2,
Bind2.QUICKSTART_FEATURES);
final boolean usingFast = quickStartMechanism instanceof HashedToken; final boolean usingFast = quickStartMechanism instanceof HashedToken;
final Element authenticate = final Element authenticate =
generateAuthenticationRequest(quickStartMechanism.getClientFirstMessage(sslSocketOrNull(this.socket)), usingFast); generateAuthenticationRequest(
quickStartMechanism.getClientFirstMessage(sslSocketOrNull(this.socket)),
usingFast);
authenticate.setAttribute("mechanism", quickStartMechanism.getMechanism()); authenticate.setAttribute("mechanism", quickStartMechanism.getMechanism());
sendStartStream(true, false); sendStartStream(true, false);
synchronized (this.mStanzaQueue) { synchronized (this.mStanzaQueue) {
@ -2377,7 +2417,13 @@ public class XmppConnection implements Runnable {
++stanzasSent; ++stanzasSent;
if (Config.EXTENDED_SM_LOGGING) { if (Config.EXTENDED_SM_LOGGING) {
Log.d(Config.LOGTAG, account.getJid().asBareJid()+": counting outbound "+packet.getName()+" as #" + stanzasSent); Log.d(
Config.LOGTAG,
account.getJid().asBareJid()
+ ": counting outbound "
+ packet.getName()
+ " as #"
+ stanzasSent);
} }
this.mStanzaQueue.append(stanzasSent, stanza); this.mStanzaQueue.append(stanzasSent, stanza);
if (stanza instanceof MessagePacket && stanza.getId() != null && inSmacksSession) { if (stanza instanceof MessagePacket && stanza.getId() != null && inSmacksSession) {
@ -2791,7 +2837,8 @@ public class XmppConnection implements Runnable {
public boolean sm() { public boolean sm() {
return streamId != null return streamId != null
|| (connection.streamFeatures != null || (connection.streamFeatures != null
&& connection.streamFeatures.hasChild("sm", Namespace.STREAM_MANAGEMENT)); && connection.streamFeatures.hasChild(
"sm", Namespace.STREAM_MANAGEMENT));
} }
public boolean csi() { public boolean csi() {
@ -2912,7 +2959,8 @@ public class XmppConnection implements Runnable {
} }
public boolean bookmarks2() { public boolean bookmarks2() {
return pepPublishOptions() && hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT); return pepPublishOptions()
&& hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT);
} }
public boolean externalServiceDiscovery() { public boolean externalServiceDiscovery() {