check sasl success message no response

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java | 34 ++++
1 file changed, 29 insertions(+), 5 deletions(-)

Detailed changes

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<String> 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 {