CVE-2016-10027: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') (CWE-362)
value: Race condition in the XMPP library in Smack before 4.1.9, when the SecurityMode.required TLS setting has been set, allows man-in-the-middle attackers to bypass TLS protections and trigger use of cleartext for client authentication by stripping the "starttls" feature from a server response.
CWE description: The product contains a code sequence that can run concurrently with other code, and the code sequence requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence that is operating concurrently.
CVSS v3 base score: 5.9
Published: 2017-01-12T23:59Z
Repository: https://github.com/igniterealtime/Smack
Language: Java
Files changed: smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java, smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
Commit date: 2016-11-14 20:31:13 +0100
Commit message: Move TLS Required check at the end of connect()

It was a *very* bad idea to perform the SecurityMode.Required check in
the connection's reader thread and not at the end of
AbstractXMPPConnectin's connect(). :/

This behavior dates back to 8e750912a765f77a4f178a4f307a8b42c2afb5ae

Fixes SMACK-739

Diff: diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
index 7b680b1a3..df1acef66 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
@@ -360,10 +360,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
         lastFeaturesReceived.init();
         streamId = null;
 
         // Perform the actual connection to the XMPP service
         connectInternal();
+
         return this;
     }
 
     /**
      * Abstract method that concrete subclasses of XMPPConnection need to implement to perform their
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
index d0ca18bf2..4d21f6fc6 100644
--- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
@@ -26,14 +26,13 @@ import org.jivesoftware.smack.SmackConfiguration;
 import org.jivesoftware.smack.SmackException;
 import org.jivesoftware.smack.SmackException.AlreadyConnectedException;
 import org.jivesoftware.smack.SmackException.AlreadyLoggedInException;
 import org.jivesoftware.smack.SmackException.NoResponseException;
 import org.jivesoftware.smack.SmackException.NotConnectedException;
-import org.jivesoftware.smack.SmackException.ConnectionException;
 import org.jivesoftware.smack.SmackException.SecurityRequiredByClientException;
+import org.jivesoftware.smack.SmackException.ConnectionException;
 import org.jivesoftware.smack.SmackException.SecurityRequiredByServerException;
-import org.jivesoftware.smack.SmackException.SecurityRequiredException;
 import org.jivesoftware.smack.SynchronizationPoint;
 import org.jivesoftware.smack.XMPPException.StreamErrorException;
 import org.jivesoftware.smack.XMPPConnection;
 import org.jivesoftware.smack.XMPPException;
 import org.jivesoftware.smack.XMPPException.XMPPErrorException;
@@ -855,10 +854,18 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
         initConnection();
 
         // Wait with SASL auth until the SASL mechanisms have been received
         saslFeatureReceived.checkIfSuccessOrWaitOrThrow();
 
+        // If TLS is required but the server doesn't offer it, disconnect
+        // from the server and throw an error. First check if we've already negotiated TLS
+        // and are secure, however (features get parsed a second time after TLS is established).
+        if (!isSecureConnection() && getConfiguration().getSecurityMode() == SecurityMode.required) {
+            shutdown();
+            throw new SecurityRequiredByClientException();
+        }
+
         // Make note of the fact that we're now connected.
         connected = true;
         callConnectionConnectedListener();
 
         // Automatically makes the login if the user was previously connected successfully
@@ -895,11 +902,11 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
     protected void setWriter(Writer writer) {
         this.writer = writer;
     }
 
     @Override
-    protected void afterFeaturesReceived() throws SecurityRequiredException, NotConnectedException {
+    protected void afterFeaturesReceived() throws NotConnectedException {
         StartTls startTlsFeature = getFeature(StartTls.ELEMENT, StartTls.NAMESPACE);
         if (startTlsFeature != null) {
             if (startTlsFeature.required() && config.getSecurityMode() == SecurityMode.disabled) {
                 notifyConnectionError(new SecurityRequiredByServerException());
                 return;
@@ -907,17 +914,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
 
             if (config.getSecurityMode() != ConnectionConfiguration.SecurityMode.disabled) {
                 send(new StartTls());
             }
         }
-        // If TLS is required but the server doesn't offer it, disconnect
-        // from the server and throw an error. First check if we've already negotiated TLS
-        // and are secure, however (features get parsed a second time after TLS is established).
-        if (!isSecureConnection() && startTlsFeature == null
-                        && getConfiguration().getSecurityMode() == SecurityMode.required) {
-            throw new SecurityRequiredByClientException();
-        }
 
         if (getSASLAuthentication().authenticationSuccessful()) {
             // If we have received features after the SASL has been successfully completed, then we
             // have also *maybe* received, as it is an optional feature, the compression feature
             // from the server.

Vulnerable code: import org.jivesoftware.smack.SmackException.ConnectionException;
import org.jivesoftware.smack.SmackException.SecurityRequiredException;
    protected void afterFeaturesReceived() throws SecurityRequiredException, NotConnectedException {
        // If TLS is required but the server doesn't offer it, disconnect
        // from the server and throw an error. First check if we've already negotiated TLS
        // and are secure, however (features get parsed a second time after TLS is established).
        if (!isSecureConnection() && startTlsFeature == null
                        && getConfiguration().getSecurityMode() == SecurityMode.required) {
            throw new SecurityRequiredByClientException();
        }
Fixed code: 
import org.jivesoftware.smack.SmackException.ConnectionException;
        // If TLS is required but the server doesn't offer it, disconnect
        // from the server and throw an error. First check if we've already negotiated TLS
        // and are secure, however (features get parsed a second time after TLS is established).
        if (!isSecureConnection() && getConfiguration().getSecurityMode() == SecurityMode.required) {
            shutdown();
            throw new SecurityRequiredByClientException();
        }

    protected void afterFeaturesReceived() throws NotConnectedException {