aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/eu/siacs/conversations/xmpp
diff options
context:
space:
mode:
authorChristian S <christian@pix-art.de>2016-01-13 17:48:30 +0100
committerChristian S <christian@pix-art.de>2016-01-13 17:48:30 +0100
commit4c2dc7082855581d875c51d97650c7b4a435abe5 (patch)
tree818ebaea4bfa9743e0acf85abc92afca7b51449a /src/main/java/eu/siacs/conversations/xmpp
parentc133b1d7e1cc9a3a3426d26e35cc038b508e1691 (diff)
parent3b3aa5b5841494ba02fe327f910558eb40e662b5 (diff)
Merge branch 'siacs/master' into development
Diffstat (limited to 'src/main/java/eu/siacs/conversations/xmpp')
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java178
1 files changed, 109 insertions, 69 deletions
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index c64fa196d..a56a64aff 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -20,7 +20,6 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.math.BigInteger;
import java.net.ConnectException;
import java.net.IDN;
@@ -35,12 +34,9 @@ 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.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
@@ -66,6 +62,7 @@ import eu.siacs.conversations.generator.IqGenerator;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.DNSHelper;
+import eu.siacs.conversations.utils.SSLSocketHelper;
import eu.siacs.conversations.utils.SocksSocketFactory;
import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
@@ -247,6 +244,7 @@ public class XmppConnection implements Runnable {
}
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": connect to "+destination+" via TOR");
socket = SocksSocketFactory.createSocketOverTor(destination,account.getPort());
+ startXmpp();
} else if (DNSHelper.isIp(account.getServer().toString())) {
socket = new Socket();
try {
@@ -254,13 +252,12 @@ public class XmppConnection implements Runnable {
} catch (IOException e) {
throw new UnknownHostException();
}
+ startXmpp();
} else {
- final Bundle result = DNSHelper.getSRVRecord(account.getServer(),mXmppConnectionService);
+ final Bundle result = DNSHelper.getSRVRecord(account.getServer(), mXmppConnectionService);
final ArrayList<Parcelable>values = result.getParcelableArrayList("values");
- int i = 0;
- boolean socketError = true;
- while (socketError && values.size() > i) {
- final Bundle namePort = (Bundle) values.get(i);
+ for(Iterator<Parcelable> iterator = values.iterator(); iterator.hasNext();) {
+ final Bundle namePort = (Bundle) iterator.next();
try {
String srvRecordServer;
try {
@@ -271,48 +268,57 @@ public class XmppConnection implements Runnable {
}
final int srvRecordPort = namePort.getInt("port");
final String srvIpServer = namePort.getString("ip");
+ // if tls is true, encryption is implied and must not be started
+ features.encryptionEnabled = namePort.getBoolean("tls");
final InetSocketAddress addr;
if (srvIpServer != null) {
addr = new InetSocketAddress(srvIpServer, srvRecordPort);
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ ": using values from dns " + srvRecordServer
- + "[" + srvIpServer + "]:" + srvRecordPort);
+ + "[" + srvIpServer + "]:" + srvRecordPort + " tls: " + features.encryptionEnabled);
} else {
addr = new InetSocketAddress(srvRecordServer, srvRecordPort);
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ ": using values from dns "
- + srvRecordServer + ":" + srvRecordPort);
+ + srvRecordServer + ":" + srvRecordPort + " tls: " + features.encryptionEnabled);
}
- socket = new Socket();
- socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
- socketError = false;
+
+ if (!features.encryptionEnabled) {
+ socket = new Socket();
+ socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
+ } else {
+ final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
+ socket = tlsFactoryVerifier.factory.createSocket();
+
+ if (socket == null) {
+ throw new IOException("could not initialize ssl socket");
+ }
+
+ SSLSocketHelper.setSecurity((SSLSocket) socket);
+ SSLSocketHelper.setSNIHost(tlsFactoryVerifier.factory, (SSLSocket) socket, account.getServer().getDomainpart());
+ SSLSocketHelper.setAlpnProtocol(tlsFactoryVerifier.factory, (SSLSocket) socket, "xmpp-client");
+
+ socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
+
+ if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) socket).getSession())) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
+ throw new SecurityException();
+ }
+ }
+
+ if (startXmpp())
+ break; // successfully connected to server that speaks xmpp
+ } catch(final SecurityException e) {
+ throw e;
} catch (final Throwable e) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage() +"("+e.getClass().getName()+")");
- i++;
+ if (!iterator.hasNext()) {
+ throw new UnknownHostException();
+ }
}
}
- if (socketError) {
- throw new UnknownHostException();
- }
- }
- final OutputStream out = socket.getOutputStream();
- tagWriter.setOutputStream(out);
- final InputStream in = socket.getInputStream();
- tagReader.setInputStream(in);
- tagWriter.beginDocument();
- sendStartStream();
- Tag nextTag;
- while ((nextTag = tagReader.readTag()) != null) {
- if (nextTag.isStart("stream")) {
- processStream();
- break;
- } else {
- throw new IOException("unknown tag on connect");
- }
- }
- if (socket.isConnected()) {
- socket.close();
}
+ processStream();
} catch (final IncompatibleServerException e) {
this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
} catch (final SecurityException e) {
@@ -344,6 +350,66 @@ public class XmppConnection implements Runnable {
}
}
+ /**
+ * Starts xmpp protocol, call after connecting to socket
+ * @return true if server returns with valid xmpp, false otherwise
+ * @throws IOException Unknown tag on connect
+ * @throws XmlPullParserException Bad Xml
+ * @throws NoSuchAlgorithmException Other error
+ */
+ private boolean startXmpp() throws IOException, XmlPullParserException, NoSuchAlgorithmException {
+ tagWriter.setOutputStream(socket.getOutputStream());
+ tagReader.setInputStream(socket.getInputStream());
+ tagWriter.beginDocument();
+ sendStartStream();
+ Tag nextTag;
+ while ((nextTag = tagReader.readTag()) != null) {
+ if (nextTag.isStart("stream")) {
+ return true;
+ } else {
+ throw new IOException("unknown tag on connect");
+ }
+ }
+ if (socket.isConnected()) {
+ socket.close();
+ }
+ return false;
+ }
+
+ private static class TlsFactoryVerifier {
+ private final SSLSocketFactory factory;
+ private final HostnameVerifier verifier;
+
+ public TlsFactoryVerifier(final SSLSocketFactory factory, final HostnameVerifier verifier) throws IOException {
+ this.factory = factory;
+ this.verifier = verifier;
+ if (factory == null || verifier == null) {
+ throw new IOException("could not setup ssl");
+ }
+ }
+ }
+
+ private TlsFactoryVerifier getTlsFactoryVerifier() throws NoSuchAlgorithmException, KeyManagementException, IOException {
+ final SSLContext sc = SSLContext.getInstance("TLS");
+ MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager();
+ KeyManager[] keyManager;
+ if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) {
+ keyManager = new KeyManager[]{mKeyManager};
+ } else {
+ keyManager = null;
+ }
+ sc.init(keyManager, new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()}, mXmppConnectionService.getRNG());
+ final SSLSocketFactory factory = sc.getSocketFactory();
+ final HostnameVerifier verifier;
+ if (mInteractive) {
+ verifier = trustManager.wrapHostnameVerifier(new XmppDomainVerifier());
+ } else {
+ verifier = trustManager.wrapHostnameVerifierNonInteractive(new XmppDomainVerifier());
+ }
+
+ return new TlsFactoryVerifier(factory, verifier);
+ }
+
@Override
public void run() {
try {
@@ -605,53 +671,27 @@ public class XmppConnection implements Runnable {
tagWriter.writeTag(startTLS);
}
+
+
private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException {
tagReader.readTag();
try {
- final SSLContext sc = SSLContext.getInstance("TLS");
- MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager();
- KeyManager[] keyManager;
- if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) {
- keyManager = new KeyManager[]{ mKeyManager };
- } else {
- keyManager = null;
- }
- sc.init(keyManager,new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()},mXmppConnectionService.getRNG());
- final SSLSocketFactory factory = sc.getSocketFactory();
- final HostnameVerifier verifier;
- if (mInteractive) {
- verifier = trustManager.wrapHostnameVerifier(new XmppDomainVerifier());
- } else {
- verifier = trustManager.wrapHostnameVerifierNonInteractive(new XmppDomainVerifier());
- }
+ final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
final InetAddress address = socket == null ? null : socket.getInetAddress();
- if (factory == null || address == null || verifier == null) {
+ if (address == null) {
throw new IOException("could not setup ssl");
}
- final SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket,address.getHostAddress(), socket.getPort(),true);
+ final SSLSocket sslSocket = (SSLSocket) tlsFactoryVerifier.factory.createSocket(socket, address.getHostAddress(), socket.getPort(), true);
if (sslSocket == null) {
throw new IOException("could not initialize ssl socket");
}
- final String[] supportProtocols;
- final Collection<String> supportedProtocols = new LinkedList<>(
- Arrays.asList(sslSocket.getSupportedProtocols()));
- supportedProtocols.remove("SSLv3");
- supportProtocols = supportedProtocols.toArray(new String[supportedProtocols.size()]);
-
- sslSocket.setEnabledProtocols(supportProtocols);
-
- final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
- sslSocket.getSupportedCipherSuites());
- //Log.d(Config.LOGTAG, "Using ciphers: " + Arrays.toString(cipherSuites));
- if (cipherSuites.length > 0) {
- sslSocket.setEnabledCipherSuites(cipherSuites);
- }
+ SSLSocketHelper.setSecurity(sslSocket);
- if (!verifier.verify(account.getServer().getDomainpart(),sslSocket.getSession())) {
+ if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), sslSocket.getSession())) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
throw new SecurityException();
}