Skip to content
Snippets Groups Projects
Commit 7a1204e0 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Added SSL Handling

parent 42fa2d26
Branches
Tags
No related merge requests found
Showing
with 222 additions and 64 deletions
......@@ -10,17 +10,17 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import de.kuschku.libquassel.events.LagChangedEvent;
import de.kuschku.libquassel.localtypes.NotificationManager;
import de.kuschku.libquassel.localtypes.backlogmanagers.BacklogManager;
import de.kuschku.libquassel.localtypes.backlogmanagers.SimpleBacklogManager;
import de.kuschku.libquassel.events.ConnectionChangeEvent;
import de.kuschku.libquassel.events.LagChangedEvent;
import de.kuschku.libquassel.events.StatusMessageEvent;
import de.kuschku.libquassel.functions.types.HandshakeFunction;
import de.kuschku.libquassel.functions.types.InitRequestFunction;
import de.kuschku.libquassel.functions.types.RpcCallFunction;
import de.kuschku.libquassel.localtypes.Buffer;
import de.kuschku.libquassel.localtypes.Buffers;
import de.kuschku.libquassel.localtypes.NotificationManager;
import de.kuschku.libquassel.localtypes.backlogmanagers.BacklogManager;
import de.kuschku.libquassel.localtypes.backlogmanagers.SimpleBacklogManager;
import de.kuschku.libquassel.message.Message;
import de.kuschku.libquassel.objects.types.ClientInitAck;
import de.kuschku.libquassel.objects.types.ClientLogin;
......@@ -28,7 +28,6 @@ import de.kuschku.libquassel.objects.types.SessionState;
import de.kuschku.libquassel.primitives.types.BufferInfo;
import de.kuschku.libquassel.primitives.types.QVariant;
import de.kuschku.libquassel.syncables.types.BufferSyncer;
import de.kuschku.libquassel.syncables.types.BufferViewConfig;
import de.kuschku.libquassel.syncables.types.BufferViewManager;
import de.kuschku.libquassel.syncables.types.Identity;
import de.kuschku.libquassel.syncables.types.IgnoreListManager;
......@@ -273,15 +272,15 @@ public class Client {
return notificationManager;
}
public long getLag() {
return lag;
}
public void setLag(long l) {
lag = l;
busProvider.sendEvent(new LagChangedEvent(lag));
}
public long getLag() {
return lag;
}
public IgnoreListManager getIgnoreListManager() {
return ignoreListManager;
}
......
......@@ -21,6 +21,7 @@ import java.util.concurrent.Executors;
import de.kuschku.libquassel.events.ConnectionChangeEvent;
import de.kuschku.libquassel.events.GeneralErrorEvent;
import de.kuschku.libquassel.events.HandshakeFailedEvent;
import de.kuschku.libquassel.events.UnknownCertificateEvent;
import de.kuschku.libquassel.functions.types.HandshakeFunction;
import de.kuschku.libquassel.functions.types.Heartbeat;
import de.kuschku.libquassel.objects.types.ClientInit;
......@@ -30,6 +31,8 @@ import de.kuschku.libquassel.primitives.types.Protocol;
import de.kuschku.libquassel.protocols.DatastreamPeer;
import de.kuschku.libquassel.protocols.LegacyPeer;
import de.kuschku.libquassel.protocols.RemotePeer;
import de.kuschku.libquassel.ssl.CertificateManager;
import de.kuschku.libquassel.ssl.UnknownCertificateException;
import de.kuschku.util.ServerAddress;
import de.kuschku.util.niohelpers.WrappedChannel;
......@@ -66,11 +69,14 @@ public class CoreConnection {
private ConnectionChangeEvent.Status status = ConnectionChangeEvent.Status.DISCONNECTED;
@Nullable
private Client client;
@NonNull
private CertificateManager certificateManager;
public CoreConnection(@NonNull final ServerAddress address, @NonNull final ClientData clientData, @NonNull final BusProvider busProvider) {
public CoreConnection(@NonNull final ServerAddress address, @NonNull final ClientData clientData, @NonNull final BusProvider busProvider, @NonNull CertificateManager certificateManager) {
this.address = address;
this.clientData = clientData;
this.busProvider = busProvider;
this.certificateManager = certificateManager;
}
@NonNull
......@@ -107,10 +113,8 @@ public class CoreConnection {
/**
* Closes the connection and interrupts all threads this connection has spawned.
*
* @throws IOException
*/
public void close() throws IOException {
public void close() {
assertNotNull(client);
client.setConnectionStatus(ConnectionChangeEvent.Status.DISCONNECTED);
......@@ -121,8 +125,12 @@ public class CoreConnection {
if (outputExecutor != null) outputExecutor.shutdownNow();
// Which we do exactly here
try {
if (channel != null) channel.close();
if (socket != null) socket.close();
} catch (Exception e) {
// We won’t report these issues, as these don’t matter to us anyway anymore
}
}
@Nullable
......@@ -177,11 +185,7 @@ public class CoreConnection {
}
public void onEventAsync(HandshakeFailedEvent event) {
try {
this.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void onEventAsync(@NonNull ConnectionChangeEvent event) {
......@@ -189,7 +193,23 @@ public class CoreConnection {
}
public void setCompression(boolean supportsCompression) {
if (supportsCompression) channel = WrappedChannel.withCompression(getChannel());
if (supportsCompression)
channel = WrappedChannel.withCompression(getChannel());
}
private void setSSL(boolean supportsSSL) {
if (supportsSSL) {
try {
channel = WrappedChannel.withSSL(getChannel(), certificateManager, address);
} catch (Exception e) {
if (e.getCause() instanceof UnknownCertificateException) {
busProvider.sendEvent(new UnknownCertificateEvent((UnknownCertificateException) e.getCause()));
} else {
busProvider.sendEvent(new GeneralErrorEvent(e));
}
close();
}
}
}
public void setClient(@NonNull Client client) {
......@@ -220,6 +240,8 @@ public class CoreConnection {
final Protocol protocol = ProtocolSerializer.get().deserialize(buffer);
// Wrap socket in SSL context if ssl is enabled
setSSL(protocol.protocolFlags.supportsSSL);
// Wrap socket in deflater if compression is enabled
setCompression(protocol.protocolFlags.supportsCompression);
......
......@@ -5,14 +5,11 @@ import android.util.Log;
import org.joda.time.DateTime;
import java.util.List;
import de.kuschku.libquassel.events.ConnectionChangeEvent;
import de.kuschku.libquassel.events.GeneralErrorEvent;
import de.kuschku.libquassel.events.HandshakeFailedEvent;
import de.kuschku.libquassel.events.LoginFailedEvent;
import de.kuschku.libquassel.events.LoginSuccessfulEvent;
import de.kuschku.libquassel.exceptions.UnknownTypeException;
import de.kuschku.libquassel.functions.types.Heartbeat;
import de.kuschku.libquassel.functions.types.HeartbeatReply;
import de.kuschku.libquassel.functions.types.InitDataFunction;
......@@ -26,7 +23,6 @@ import de.kuschku.libquassel.objects.types.ClientLoginReject;
import de.kuschku.libquassel.objects.types.SessionInit;
import de.kuschku.libquassel.primitives.types.BufferInfo;
import de.kuschku.libquassel.syncables.SyncableRegistry;
import de.kuschku.libquassel.syncables.types.BufferViewConfig;
import de.kuschku.libquassel.syncables.types.Identity;
import de.kuschku.libquassel.syncables.types.SyncableObject;
import de.kuschku.util.AndroidAssert;
......
package de.kuschku.libquassel.events;
import java.security.cert.X509Certificate;
public class CertificateAcceptedEvent {
public final X509Certificate certificate;
public CertificateAcceptedEvent(X509Certificate certificate) {
this.certificate = certificate;
}
}
package de.kuschku.libquassel.events;
import java.security.cert.X509Certificate;
import de.kuschku.libquassel.ssl.UnknownCertificateException;
import de.kuschku.util.ServerAddress;
public class UnknownCertificateEvent {
public final X509Certificate certificate;
public final ServerAddress address;
public UnknownCertificateEvent(X509Certificate certificate, ServerAddress address) {
this.certificate = certificate;
this.address = address;
}
public UnknownCertificateEvent(UnknownCertificateException cause) {
this(cause.certificate, cause.address);
}
}
......@@ -5,11 +5,9 @@ import android.support.annotation.NonNull;
import org.joda.time.DateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import de.kuschku.libquassel.functions.FunctionType;
import de.kuschku.libquassel.functions.types.Heartbeat;
import de.kuschku.libquassel.functions.types.HeartbeatReply;
import de.kuschku.libquassel.primitives.types.QVariant;
......
......@@ -136,6 +136,14 @@ public class BacklogFilter implements UICallback {
}
}
public int getFilters() {
int filters = 0x00000000;
for (Message.Type type : filteredTypes) {
filters |= type.value;
}
return filters;
}
public void setFilters(int filters) {
Set<Message.Type> removed = new HashSet<>();
for (Message.Type type : filteredTypes) {
......@@ -152,12 +160,4 @@ public class BacklogFilter implements UICallback {
}
}
}
public int getFilters() {
int filters = 0x00000000;
for (Message.Type type : filteredTypes) {
filters |= type.value;
}
return filters;
}
}
......@@ -2,15 +2,12 @@ package de.kuschku.libquassel.localtypes.backlogmanagers;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import java.util.List;
import de.kuschku.libquassel.Client;
import de.kuschku.libquassel.message.Message;
import de.kuschku.libquassel.syncables.types.SyncableObject;
import de.kuschku.util.observables.AutoScroller;
import de.kuschku.util.observables.lists.ObservableSortedList;
public abstract class BacklogManager<T extends BacklogManager<T>> extends SyncableObject<T> {
......
......@@ -3,7 +3,6 @@ package de.kuschku.libquassel.primitives.serializers;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import de.kuschku.libquassel.primitives.types.BufferInfo;
import org.joda.time.DateTime;
import java.io.IOException;
......@@ -11,6 +10,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import de.kuschku.libquassel.message.Message;
import de.kuschku.libquassel.primitives.types.BufferInfo;
import static de.kuschku.util.AndroidAssert.assertNotNull;
......
......@@ -2,7 +2,6 @@ package de.kuschku.libquassel.protocols;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
......@@ -14,11 +13,9 @@ import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
......@@ -52,6 +49,7 @@ import de.kuschku.libquassel.primitives.types.QVariant;
import de.kuschku.util.niohelpers.Helper;
import de.kuschku.util.niohelpers.WrappedChannel;
import static de.kuschku.util.AndroidAssert.assertFalse;
import static de.kuschku.util.AndroidAssert.assertNotNull;
/**
......@@ -120,6 +118,7 @@ public class DatastreamPeer implements RemotePeer {
public void onEventBackgroundThread(@NonNull SyncFunction func) {
assertNotNull(connection.getOutputExecutor());
assertFalse(connection.getOutputExecutor().isShutdown());
connection.getOutputExecutor().submit(new OutputRunnable<>(
VariantVariantListSerializer.<SyncFunction>get(),
UnpackedSyncFunctionSerializer.get().serialize(func)
......@@ -128,6 +127,7 @@ public class DatastreamPeer implements RemotePeer {
public void onEventBackgroundThread(@NonNull RpcCallFunction func) {
assertNotNull(connection.getOutputExecutor());
assertFalse(connection.getOutputExecutor().isShutdown());
connection.getOutputExecutor().submit(new OutputRunnable<>(
VariantVariantListSerializer.<RpcCallFunction>get(),
UnpackedRpcCallFunctionSerializer.get().serialize(func)
......@@ -136,6 +136,7 @@ public class DatastreamPeer implements RemotePeer {
public void onEventBackgroundThread(@NonNull InitRequestFunction func) {
assertNotNull(connection.getOutputExecutor());
assertFalse(connection.getOutputExecutor().isShutdown());
connection.getOutputExecutor().submit(new OutputRunnable<>(
VariantVariantListSerializer.<InitRequestFunction>get(),
InitRequestFunctionSerializer.get().serializePacked(func)
......@@ -144,6 +145,7 @@ public class DatastreamPeer implements RemotePeer {
public void onEventBackgroundThread(@NonNull InitDataFunction func) {
assertNotNull(connection.getOutputExecutor());
assertFalse(connection.getOutputExecutor().isShutdown());
connection.getOutputExecutor().submit(new OutputRunnable<>(
VariantVariantListSerializer.<InitDataFunction>get(),
InitDataFunctionSerializer.get().serialize(func)
......@@ -152,6 +154,7 @@ public class DatastreamPeer implements RemotePeer {
public void onEventBackgroundThread(@NonNull Heartbeat func) {
assertNotNull(connection.getOutputExecutor());
assertFalse(connection.getOutputExecutor().isShutdown());
connection.getOutputExecutor().submit(new OutputRunnable<>(
VariantVariantListSerializer.<InitDataFunction>get(),
HeartbeatSerializer.get().serialize(func)
......@@ -160,6 +163,7 @@ public class DatastreamPeer implements RemotePeer {
public void onEventBackgroundThread(@NonNull HeartbeatReply func) {
assertNotNull(connection.getOutputExecutor());
assertFalse(connection.getOutputExecutor().isShutdown());
connection.getOutputExecutor().submit(new OutputRunnable<>(
VariantVariantListSerializer.<InitDataFunction>get(),
HeartbeatReplySerializer.get().serialize(func)
......@@ -168,6 +172,7 @@ public class DatastreamPeer implements RemotePeer {
public void onEventBackgroundThread(@NonNull HandshakeFunction func) {
assertNotNull(connection.getOutputExecutor());
assertFalse(connection.getOutputExecutor().isShutdown());
Map<String, QVariant> variantMap = MessageTypeRegistry.toVariantMap(func.data).data;
assertNotNull(variantMap);
connection.getOutputExecutor().submit(new OutputRunnable<>(
......@@ -273,6 +278,7 @@ public class DatastreamPeer implements RemotePeer {
private class ParseRunnable implements Runnable {
ByteBuffer buffer;
public ParseRunnable(ByteBuffer buffer) {
this.buffer = buffer;
}
......
......@@ -13,7 +13,6 @@ import java.util.concurrent.Executors;
import de.kuschku.libquassel.BusProvider;
import de.kuschku.libquassel.CoreConnection;
import de.kuschku.libquassel.events.ConnectionChangeEvent;
import de.kuschku.libquassel.events.GeneralErrorEvent;
import de.kuschku.libquassel.functions.FunctionType;
import de.kuschku.libquassel.functions.serializers.HeartbeatReplySerializer;
......@@ -37,12 +36,11 @@ import de.kuschku.libquassel.primitives.serializers.PrimitiveSerializer;
import de.kuschku.libquassel.primitives.serializers.VariantSerializer;
import de.kuschku.libquassel.primitives.serializers.VariantVariantListSerializer;
import de.kuschku.libquassel.primitives.types.QVariant;
import de.kuschku.util.AndroidAssert;
import de.kuschku.util.niohelpers.WrappedChannel;
import static de.kuschku.libquassel.primitives.QMetaType.Type.QVariantList;
import static de.kuschku.libquassel.primitives.QMetaType.Type.QVariantMap;
import static de.kuschku.util.AndroidAssert.*;
import static de.kuschku.util.AndroidAssert.assertNotNull;
/**
* A helper class processing incoming and outgoing messages.
......@@ -175,6 +173,7 @@ public class LegacyPeer implements RemotePeer {
private class ParseRunnable implements Runnable {
ByteBuffer buffer;
public ParseRunnable(ByteBuffer buffer) {
this.buffer = buffer;
}
......
package de.kuschku.libquassel.ssl;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import de.kuschku.util.ServerAddress;
public interface CertificateManager {
boolean isTrusted(X509Certificate certificate, ServerAddress core);
boolean addCertificate(X509Certificate certificate, ServerAddress core);
boolean removeCertificate(X509Certificate certificate, ServerAddress core);
boolean removeAllCertificates(ServerAddress core);
List<String> findCertificates(ServerAddress core);
Map<String, Collection<String>> findAllCertificates();
void checkTrusted(X509Certificate certificate, ServerAddress address) throws UnknownCertificateException;
}
package de.kuschku.libquassel.ssl;
import android.support.annotation.NonNull;
import android.util.Log;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import de.kuschku.util.ServerAddress;
import de.kuschku.util.certificates.CertificateUtils;
public class QuasselTrustManager implements X509TrustManager {
@NonNull
private final X509TrustManager wrapped;
@NonNull
private final CertificateManager certificateManager;
@NonNull
private ServerAddress address;
public QuasselTrustManager(@NonNull X509TrustManager wrapped, @NonNull CertificateManager certificateManager, @NonNull ServerAddress address) {
this.wrapped = wrapped;
this.certificateManager = certificateManager;
this.address = address;
}
public static QuasselTrustManager fromFactory(@NonNull TrustManagerFactory factory, @NonNull CertificateManager certificateManager, @NonNull ServerAddress address) throws GeneralSecurityException {
TrustManager[] managers = factory.getTrustManagers();
for (TrustManager manager : managers) {
if (manager instanceof X509TrustManager) {
return new QuasselTrustManager((X509TrustManager) manager, certificateManager, address);
}
}
throw new GeneralSecurityException("Couldn’t find trustmanager provided by factory");
}
public static QuasselTrustManager fromDefault(@NonNull CertificateManager certificateManager, @NonNull ServerAddress address) throws GeneralSecurityException {
TrustManagerFactory factory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
factory.init((KeyStore) null);
return fromFactory(factory, certificateManager, address);
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
wrapped.checkClientTrusted(chain, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
wrapped.checkServerTrusted(chain, authType);
chain[0].checkValidity();
if (!CertificateUtils.getHostnames(chain[0]).contains(address.host))
throw new CertificateException("Hostname not in certificate");
} catch (CertificateException e) {
certificateManager.checkTrusted(chain[0], address);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return wrapped.getAcceptedIssuers();
}
}
package de.kuschku.libquassel.ssl;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import de.kuschku.util.ServerAddress;
public class UnknownCertificateException extends CertificateException {
public final X509Certificate certificate;
public final ServerAddress address;
public UnknownCertificateException(X509Certificate certificate, ServerAddress address) {
this.certificate = certificate;
this.address = address;
}
}
......@@ -17,15 +17,15 @@ public class AliasManagerSerializer implements ObjectSerializer<AliasManager> {
@NonNull
private static final AliasManagerSerializer serializer = new AliasManagerSerializer();
private AliasManagerSerializer() {
}
@NonNull
public static AliasManagerSerializer get() {
return serializer;
}
private AliasManagerSerializer() {
}
@Nullable
@Override
public QVariant<Map<String, QVariant>> toVariantMap(@NonNull AliasManager data) {
......
......@@ -15,14 +15,15 @@ import de.kuschku.libquassel.syncables.types.IgnoreListManager;
public class IgnoreListManagerSerializer implements ObjectSerializer<IgnoreListManager> {
private static IgnoreListManagerSerializer serializer = new IgnoreListManagerSerializer();
public static IgnoreListManagerSerializer get() {
return serializer;
}
private IgnoreListManagerSerializer() {
}
public static IgnoreListManagerSerializer get() {
return serializer;
}
@Nullable
@Override
public QVariant<Map<String, QVariant>> toVariantMap(@NonNull IgnoreListManager data) {
......
......@@ -11,18 +11,18 @@ import de.kuschku.libquassel.functions.types.UnpackedFunction;
import de.kuschku.libquassel.objects.serializers.ObjectSerializer;
import de.kuschku.libquassel.primitives.types.QVariant;
import de.kuschku.libquassel.syncables.types.NetworkConfig;
import de.kuschku.libquassel.syncables.types.SyncableObject;
public class NetworkConfigSerializer implements ObjectSerializer<NetworkConfig> {
private static NetworkConfigSerializer serializer = new NetworkConfigSerializer();
public static NetworkConfigSerializer get() {
return serializer;
}
private NetworkConfigSerializer() {
}
public static NetworkConfigSerializer get() {
return serializer;
}
@Nullable
@Override
public QVariant<Map<String, QVariant>> toVariantMap(@NonNull NetworkConfig data) {
......
......@@ -11,7 +11,6 @@ import de.kuschku.libquassel.Client;
import de.kuschku.libquassel.functions.types.InitDataFunction;
import de.kuschku.libquassel.message.Message;
import de.kuschku.libquassel.primitives.types.QVariant;
import de.kuschku.libquassel.syncables.serializers.AliasManagerSerializer;
import de.kuschku.libquassel.syncables.serializers.BufferSyncerSerializer;
import de.kuschku.util.observables.lists.ObservableSortedList;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment