process authorization id in case full jid changes

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/xmpp/Jid.java            |   3 
src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java | 133 +++-
2 files changed, 81 insertions(+), 55 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/xmpp/Jid.java 🔗

@@ -118,8 +118,7 @@ public interface Jid extends Comparable<Jid>, Serializable, CharSequence {
     static Jid ofEscaped(CharSequence jid) {
         try {
             return new WrappedJid(JidCreate.from(jid));
-        } catch (XmppStringprepException e) {
-            e.printStackTrace();
+        } catch (final XmppStringprepException e) {
             throw new IllegalArgumentException(e);
         }
     }

src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java 🔗

@@ -470,61 +470,10 @@ public class XmppConnection implements Runnable {
                 switchOverToTls();
             } else if (nextTag.isStart("success")) {
                 final Element success = tagReader.readElement(nextTag);
-                final SaslMechanism.Version version;
-                try {
-                    version = SaslMechanism.Version.of(success);
-                } catch (final IllegalArgumentException e) {
-                    throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
-                }
-                final String challenge;
-                if (version == SaslMechanism.Version.SASL) {
-                    challenge = success.getContent();
-                } else if (version == SaslMechanism.Version.SASL_2) {
-                    challenge = success.findChildContent("additional-data");
-                } else {
-                    throw new AssertionError("Missing implementation for " + version);
-                }
-                try {
-                    saslMechanism.getResponse(challenge);
-                } catch (final SaslMechanism.AuthenticationException e) {
-                    Log.e(Config.LOGTAG, String.valueOf(e));
-                    throw new StateChangingException(Account.State.UNAUTHORIZED);
-                }
-                Log.d(
-                        Config.LOGTAG,
-                        account.getJid().asBareJid().toString()
-                                + ": logged in (using "
-                                + version
-                                + ")");
-                account.setKey(
-                        Account.PINNED_MECHANISM_KEY, String.valueOf(saslMechanism.getPriority()));
-                if (version == SaslMechanism.Version.SASL_2) {
-                    final String authorizationIdentifier =
-                            success.findChildContent("authorization-identifier");
-                    Log.d(
-                            Config.LOGTAG,
-                            account.getJid().asBareJid()
-                                    + ": SASL 2.0 authorization identifier was "
-                                    + authorizationIdentifier);
-                    final Element resumed = success.findChild("resumed", "urn:xmpp:sm:3");
-                    final Element failed = success.findChild("failed", "urn:xmpp:sm:3");
-                    if (resumed != null && streamId != null) {
-                        processResumed(resumed);
-                    } else if (failed != null) {
-                        processFailed(failed, false); // wait for new stream features
-                    }
-                }
-                if (version == SaslMechanism.Version.SASL) {
-                    tagReader.reset();
-                    sendStartStream();
-                    final Tag tag = tagReader.readTag();
-                    if (tag != null && tag.isStart("stream")) {
-                        processStream();
-                    } else {
-                        throw new StateChangingException(Account.State.STREAM_OPENING_ERROR);
-                    }
+                if (processSuccess(success)) {
                     break;
                 }
+
             } else if (nextTag.isStart("failure", Namespace.TLS)) {
                 throw new StateChangingException(Account.State.TLS_ERROR);
             } else if (nextTag.isStart("failure")) {
@@ -679,6 +628,84 @@ public class XmppConnection implements Runnable {
         }
     }
 
+    private boolean processSuccess(final Element success) throws IOException, XmlPullParserException {
+        final SaslMechanism.Version version;
+        try {
+            version = SaslMechanism.Version.of(success);
+        } catch (final IllegalArgumentException e) {
+            throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
+        }
+        final String challenge;
+        if (version == SaslMechanism.Version.SASL) {
+            challenge = success.getContent();
+        } else if (version == SaslMechanism.Version.SASL_2) {
+            challenge = success.findChildContent("additional-data");
+        } else {
+            throw new AssertionError("Missing implementation for " + version);
+        }
+        try {
+            saslMechanism.getResponse(challenge);
+        } catch (final SaslMechanism.AuthenticationException e) {
+            Log.e(Config.LOGTAG, String.valueOf(e));
+            throw new StateChangingException(Account.State.UNAUTHORIZED);
+        }
+        Log.d(
+                Config.LOGTAG,
+                account.getJid().asBareJid().toString()
+                        + ": logged in (using "
+                        + version
+                        + ")");
+        account.setKey(
+                Account.PINNED_MECHANISM_KEY, String.valueOf(saslMechanism.getPriority()));
+        if (version == SaslMechanism.Version.SASL_2) {
+            final String authorizationIdentifier =
+                    success.findChildContent("authorization-identifier");
+            final Jid authorizationJid;
+            try {
+                authorizationJid = Strings.isNullOrEmpty(authorizationIdentifier) ? null : Jid.ofEscaped(authorizationIdentifier);
+            } catch (final IllegalArgumentException e) {
+                Log.d(Config.LOGTAG,account.getJid().asBareJid()+": SASL 2.0 authorization identifier was not a valid jid");
+                throw new StateChangingException(Account.State.BIND_FAILURE);
+            }
+            if (authorizationJid == null) {
+                throw new StateChangingException(Account.State.BIND_FAILURE);
+            }
+            Log.d(
+                    Config.LOGTAG,
+                    account.getJid().asBareJid()
+                            + ": SASL 2.0 authorization identifier was "
+                            + authorizationJid);
+            if (!account.getJid().getDomain().equals(authorizationJid.getDomain())) {
+                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": server tried to re-assign domain to " + authorizationJid.getDomain());
+                throw new StateChangingError(Account.State.BIND_FAILURE);
+            }
+            if (authorizationJid.isFullJid() && account.setJid(authorizationJid)) {
+                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": jid changed during SASL 2.0. updating database");
+                mXmppConnectionService.databaseBackend.updateAccount(account);
+            }
+            final Element resumed = success.findChild("resumed", "urn:xmpp:sm:3");
+            final Element failed = success.findChild("failed", "urn:xmpp:sm:3");
+            if (resumed != null && streamId != null) {
+                processResumed(resumed);
+            } else if (failed != null) {
+                processFailed(failed, false); // wait for new stream features
+            }
+        }
+        if (version == SaslMechanism.Version.SASL) {
+            tagReader.reset();
+            sendStartStream();
+            final Tag tag = tagReader.readTag();
+            if (tag != null && tag.isStart("stream")) {
+                processStream();
+                return true;
+            } else {
+                throw new StateChangingException(Account.State.STREAM_OPENING_ERROR);
+            }
+        } else {
+            return false;
+        }
+    }
+
     private void processResumed(final Element resumed) throws StateChangingException {
         this.inSmacksSession = true;
         this.isBound = true;