diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index ef065c59d..541935712 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -623,6 +623,8 @@ public class XmppConnection implements Runnable { } else if (nextTag.isStart("challenge")) { final Element challenge = tagReader.readElement(nextTag); processChallenge(challenge); + } else if (!LoginInfo.isSuccess(this.loginInfo)) { + throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER); } else if (this.streamId != null && nextTag.isStart("resumed", Namespace.STREAM_MANAGEMENT)) { final Element resumed = tagReader.readElement(nextTag); @@ -730,9 +732,13 @@ public class XmppConnection implements Runnable { } else { throw new AssertionError("Missing implementation for " + version); } + final LoginInfo currentLoginInfo = this.loginInfo; + if (currentLoginInfo == null || LoginInfo.isSuccess(currentLoginInfo)) { + throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER); + } try { response.setContent( - this.loginInfo.saslMechanism.getResponse( + currentLoginInfo.saslMechanism.getResponse( challenge.getContent(), sslSocketOrNull(socket))); } catch (final SaslMechanism.AuthenticationException e) { // TODO: Send auth abort tag. @@ -764,9 +770,9 @@ public class XmppConnection implements Runnable { throw new AssertionError("Missing implementation for " + version); } try { - currentSaslMechanism.getResponse(challenge, sslSocketOrNull(socket)); + currentLoginInfo.success(challenge, sslSocketOrNull(socket)); } catch (final SaslMechanism.AuthenticationException e) { - Log.e(Config.LOGTAG, String.valueOf(e)); + Log.e(Config.LOGTAG, account.getJid().asBareJid() + ": authentication failure ", e); throw new StateChangingException(Account.State.UNAUTHORIZED); } Log.d( @@ -1481,7 +1487,7 @@ public class XmppConnection implements Runnable { authenticate(SaslMechanism.Version.SASL); } else if (this.streamFeatures.hasChild("sm", Namespace.STREAM_MANAGEMENT) && isSecure - && loginInfo != null + && LoginInfo.isSuccess(loginInfo) && streamId != null && !inSmacksSession) { if (Config.EXTENDED_SM_LOGGING) { @@ -1498,7 +1504,7 @@ public class XmppConnection implements Runnable { } else if (needsBinding) { if (this.streamFeatures.hasChild("bind", Namespace.BIND) && isSecure - && loginInfo != null) { + && LoginInfo.isSuccess(loginInfo)) { sendBindRequest(); } else { Log.d( @@ -2767,6 +2773,7 @@ public class XmppConnection implements Runnable { public final SaslMechanism saslMechanism; public final SaslMechanism.Version saslVersion; public final List inlineBindFeatures; + public final AtomicBoolean success = new AtomicBoolean(false); private LoginInfo( final SaslMechanism saslMechanism, @@ -2785,6 +2792,23 @@ public class XmppConnection implements Runnable { public static SaslMechanism mechanism(final LoginInfo loginInfo) { return loginInfo == null ? null : loginInfo.saslMechanism; } + + public void success(final String challenge, final SSLSocket sslSocket) + throws SaslMechanism.AuthenticationException { + final var response = this.saslMechanism.getResponse(challenge, sslSocket); + if (!Strings.isNullOrEmpty(response)) { + throw new SaslMechanism.AuthenticationException( + "processing success yielded another response"); + } + if (this.success.compareAndSet(false, true)) { + return; + } + throw new SaslMechanism.AuthenticationException("Process 'success' twice"); + } + + public static boolean isSuccess(final LoginInfo loginInfo) { + return loginInfo != null && loginInfo.success.get(); + } } private static class StreamId {