diff --git a/.gitmodules b/.gitmodules index 17cbe617..d0f56166 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,6 @@ [submodule "libs/openpgp-api-lib"] path = libs/openpgp-api-lib url = https://github.com/open-keychain/openpgp-api-lib.git +[submodule "libs/MemorizingTrustManager"] + path = libs/MemorizingTrustManager + url = https://github.com/ge0rg/MemorizingTrustManager diff --git a/AndroidManifest.xml b/AndroidManifest.xml index a755c98a..44bfd18d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -105,6 +105,7 @@ + diff --git a/libs/MemorizingTrustManager b/libs/MemorizingTrustManager new file mode 160000 index 00000000..7610570f --- /dev/null +++ b/libs/MemorizingTrustManager @@ -0,0 +1 @@ +Subproject commit 7610570fd89194af108b417b47951b8e97814245 diff --git a/project.properties b/project.properties index 86ae5d0c..7276fb94 100644 --- a/project.properties +++ b/project.properties @@ -14,3 +14,4 @@ target=android-19 android.library.reference.1=libs/minidns android.library.reference.2=libs/openpgp-api-lib +android.library.reference.3=libs/MemorizingTrustManager diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index 0fecbb35..03d1a48d 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -15,6 +15,8 @@ import java.util.concurrent.CopyOnWriteArrayList; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpServiceConnection; +import de.duenndns.ssl.MemorizingTrustManager; + import net.java.otr4j.OtrException; import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; @@ -90,6 +92,8 @@ public class XmppConnectionService extends Service { public static final long CARBON_GRACE_PERIOD = 60000L; private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; + + private MemorizingTrustManager mMemorizingTrustManager; private MessageParser mMessageParser = new MessageParser(this); private PresenceParser mPresenceParser = new PresenceParser(this); @@ -106,7 +110,6 @@ public class XmppConnectionService extends Service { private int convChangedListenerCount = 0; private OnAccountUpdate mOnAccountUpdate = null; private OnRosterUpdate mOnRosterUpdate = null; - private OnTLSExceptionReceived tlsException = null; public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() { @Override @@ -121,11 +124,6 @@ public class XmppConnectionService extends Service { } }; - public void setOnTLSExceptionReceivedListener( - OnTLSExceptionReceived listener) { - tlsException = listener; - } - private SecureRandom mRandom; private ContentObserver contactObserver = new ContentObserver(null) { @@ -368,6 +366,9 @@ public class XmppConnectionService extends Service { ExceptionHelper.init(getApplicationContext()); PRNGFixes.apply(); this.mRandom = new SecureRandom(); + this.mMemorizingTrustManager = new MemorizingTrustManager(getApplicationContext()); + this.mMemorizingTrustManager.wrapHostnameVerifier( + new org.apache.http.conn.ssl.StrictHostnameVerifier()); this.databaseBackend = DatabaseBackend .getInstance(getApplicationContext()); this.fileBackend = new FileBackend(getApplicationContext()); @@ -467,19 +468,6 @@ public class XmppConnectionService extends Service { connection .setOnUnregisteredIqPacketReceivedListener(this.mIqParser); connection.setOnJinglePacketReceivedListener(this.jingleListener); - connection - .setOnTLSExceptionReceivedListener(new OnTLSExceptionReceived() { - - @Override - public void onTLSExceptionReceived(String fingerprint, - Account account) { - Log.d(LOGTAG, "tls exception arrived in service"); - if (tlsException != null) { - tlsException.onTLSExceptionReceived(fingerprint, - account); - } - } - }); connection.setOnBindListener(this.mOnBindListener); return connection; } @@ -1214,10 +1202,6 @@ public class XmppConnectionService extends Service { this.databaseBackend.updateConversation(conversation); } - public void removeOnTLSExceptionReceivedListener() { - this.tlsException = null; - } - public void reconnectAccount(final Account account, final boolean force) { new Thread(new Runnable() { @@ -1338,6 +1322,10 @@ public class XmppConnectionService extends Service { public SecureRandom getRNG() { return this.mRandom; } + + public MemorizingTrustManager getMemorizingTrustManager() { + return this.mMemorizingTrustManager; + } public PowerManager getPowerManager() { return this.pm; diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java index 6fa1ec31..ed657902 100644 --- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -59,57 +59,6 @@ public class ManageAccountActivity extends XmppActivity { } }; - protected OnTLSExceptionReceived tlsExceptionReceived = new OnTLSExceptionReceived() { - - @Override - public void onTLSExceptionReceived(final String fingerprint, - final Account account) { - activity.runOnUiThread(new Runnable() { - - @Override - public void run() { - AlertDialog.Builder builder = new AlertDialog.Builder( - activity); - builder.setTitle(getString(R.string.account_status_error)); - builder.setIconAttribute(android.R.attr.alertDialogIcon); - View view = (View) getLayoutInflater().inflate( - R.layout.cert_warning, null); - TextView sha = (TextView) view.findViewById(R.id.sha); - TextView hint = (TextView) view.findViewById(R.id.hint); - StringBuilder humanReadableSha = new StringBuilder(); - humanReadableSha.append(fingerprint); - for (int i = 2; i < 59; i += 3) { - if ((i == 14) || (i == 29) || (i == 44)) { - humanReadableSha.insert(i, "\n"); - } else { - humanReadableSha.insert(i, ":"); - } - - } - hint.setText(getString(R.string.untrusted_cert_hint, - account.getServer())); - sha.setText(humanReadableSha.toString()); - builder.setView(view); - builder.setNegativeButton( - getString(R.string.certif_no_trust), null); - builder.setPositiveButton(getString(R.string.certif_trust), - new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, - int which) { - account.setSSLCertFingerprint(fingerprint); - activity.xmppConnectionService - .updateAccount(account); - } - }); - builder.create().show(); - } - }); - - } - }; - @Override protected void onCreate(Bundle savedInstanceState) { @@ -471,7 +420,6 @@ public class ManageAccountActivity extends XmppActivity { protected void onStop() { if (xmppConnectionServiceBound) { xmppConnectionService.removeOnAccountListChangedListener(); - xmppConnectionService.removeOnTLSExceptionReceivedListener(); } super.onStop(); } @@ -479,8 +427,6 @@ public class ManageAccountActivity extends XmppActivity { @Override void onBackendConnected() { xmppConnectionService.setOnAccountListChangedListener(accountChanged); - xmppConnectionService - .setOnTLSExceptionReceivedListener(tlsExceptionReceived); this.accountList.clear(); this.accountList.addAll(xmppConnectionService.getAccounts()); accountListViewAdapter.notifyDataSetChanged(); diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java index 72018394..6d2dec20 100644 --- a/src/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java @@ -21,6 +21,7 @@ import java.util.Hashtable; import java.util.List; import java.util.Map.Entry; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; @@ -28,8 +29,12 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; +import org.bouncycastle.pqc.math.linearalgebra.GoppaCode.MaMaPe; import org.xmlpull.v1.XmlPullParserException; +import de.duenndns.ssl.MemorizingTrustManager; + +import android.content.Context; import android.os.Bundle; import android.os.PowerManager; import android.os.PowerManager.WakeLock; @@ -97,11 +102,12 @@ public class XmppConnection implements Runnable { private OnIqPacketReceived unregisteredIqListener = null; private OnMessagePacketReceived messageListener = null; private OnStatusChanged statusListener = null; - private OnTLSExceptionReceived tlsListener = null; private OnBindListener bindListener = null; + private MemorizingTrustManager mMemorizingTrustManager; public XmppConnection(Account account, XmppConnectionService service) { this.mRandom = service.getRNG(); + this.mMemorizingTrustManager = service.getMemorizingTrustManager(); this.account = account; this.wakeLock = service.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, account.getJid()); @@ -440,67 +446,19 @@ public class XmppConnection implements Runnable { tagReader.readTag(); try { SSLContext sc = SSLContext.getInstance("TLS"); - TrustManagerFactory tmf = TrustManagerFactory - .getInstance(TrustManagerFactory.getDefaultAlgorithm()); - try { - tmf.init((KeyStore) null); - } catch (KeyStoreException e1) { - e1.printStackTrace(); - } - - TrustManager[] trustManagers = tmf.getTrustManagers(); - final X509TrustManager origTrustmanager = (X509TrustManager) trustManagers[0]; - - TrustManager[] wrappedTrustManagers = new TrustManager[] { new X509TrustManager() { - - @Override - public void checkClientTrusted(X509Certificate[] chain, - String authType) throws CertificateException { - origTrustmanager.checkClientTrusted(chain, authType); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, - String authType) throws CertificateException { - try { - origTrustmanager.checkServerTrusted(chain, authType); - } catch (CertificateException e) { - if (e.getCause() instanceof CertPathValidatorException) { - String sha; - try { - MessageDigest sha1 = MessageDigest - .getInstance("SHA1"); - sha1.update(chain[0].getEncoded()); - sha = CryptoHelper.bytesToHex(sha1.digest()); - if (!sha.equals(account.getSSLFingerprint())) { - changeStatus(Account.STATUS_TLS_ERROR); - if (tlsListener != null) { - tlsListener.onTLSExceptionReceived(sha, - account); - } - throw new CertificateException(); - } - } catch (NoSuchAlgorithmException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - } else { - throw new CertificateException(); - } - } - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return origTrustmanager.getAcceptedIssuers(); - } - - } }; - sc.init(null, wrappedTrustManagers, null); + sc.init(null, new X509TrustManager[] { this.mMemorizingTrustManager }, mRandom); SSLSocketFactory factory = sc.getSocketFactory(); + + HostnameVerifier verifier = this.mMemorizingTrustManager.wrapHostnameVerifier(new org.apache.http.conn.ssl.StrictHostnameVerifier()); SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true); + + if (verifier != null && !verifier.verify(account.getServer(), sslSocket.getSession())) { + Log.d(LOGTAG, account.getJid() + ": host mismatch in TLS connection"); + sslSocket.close(); + throw new IOException(); + } tagReader.setInputStream(sslSocket.getInputStream()); tagWriter.setOutputStream(sslSocket.getOutputStream()); sendStartStream(); @@ -508,10 +466,8 @@ public class XmppConnection implements Runnable { processStream(tagReader.readTag()); sslSocket.close(); } catch (NoSuchAlgorithmException e1) { - // TODO Auto-generated catch block e1.printStackTrace(); } catch (KeyManagementException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } @@ -844,11 +800,6 @@ public class XmppConnection implements Runnable { this.statusListener = listener; } - public void setOnTLSExceptionReceivedListener( - OnTLSExceptionReceived listener) { - this.tlsListener = listener; - } - public void setOnBindListener(OnBindListener listener) { this.bindListener = listener; }