Skip to content

Commit

Permalink
#162:Remove DTLSConnection when corresponding SecurityInfo is removed
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernard31 committed Mar 16, 2020
1 parent 36938b0 commit 57bbbeb
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,6 @@ public BootstrapConfig get(String endpoint, Identity deviceIdentity, BootstrapSe
@Override
public void dispose() {
super.dispose();
((EditableSecurityStore) server.getSecurityStore()).remove(getCurrentEndpoint());
((EditableSecurityStore) server.getSecurityStore()).remove(getCurrentEndpoint(), false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -366,9 +366,9 @@ public EditableSecurityStore getSecurityStore() {

@Override
public void dispose() {
getSecurityStore().remove(getCurrentEndpoint());
getSecurityStore().remove(BAD_ENDPOINT);
getSecurityStore().remove(GOOD_ENDPOINT);
getSecurityStore().remove(getCurrentEndpoint(), false);
getSecurityStore().remove(BAD_ENDPOINT, false);
getSecurityStore().remove(GOOD_ENDPOINT, false);
super.dispose();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*******************************************************************************
* Copyright (c) 2020 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
*******************************************************************************/
package org.eclipse.leshan.server.californium;

import java.security.Principal;
import java.security.PublicKey;

import javax.security.auth.x500.X500Principal;

import org.eclipse.californium.elements.auth.PreSharedKeyIdentity;
import org.eclipse.californium.elements.auth.RawPublicKeyIdentity;
import org.eclipse.californium.elements.auth.X509CertPath;
import org.eclipse.californium.elements.util.LeastRecentlyUsedCache.Predicate;
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.leshan.core.californium.EndpointContextUtil;
import org.eclipse.leshan.server.security.SecurityInfo;

/**
* This class is responsible to remove DTLS connection for a given SecurityInfo.
*/
public class ConnectionCleaner {

private DTLSConnector connector;

public ConnectionCleaner(DTLSConnector connector) {
this.connector = connector;
}

public void cleanConnectionFor(final SecurityInfo... infos) {
connector.startTerminateConnectionsForPrincipal(new Predicate<Principal>() {
@Override
public boolean accept(Principal principal) {
if (principal != null) {
for (SecurityInfo info : infos) {
if (info != null) {
// PSK
if (info.usePSK() && principal instanceof PreSharedKeyIdentity) {
String identity = ((PreSharedKeyIdentity) principal).getIdentity();
if (info.getIdentity().equals(identity)) {
return true;
}
}
// RPK
else if (info.useRPK() && principal instanceof RawPublicKeyIdentity) {
PublicKey publicKey = ((RawPublicKeyIdentity) principal).getKey();
if (info.getRawPublicKey().equals(publicKey)) {
return true;
}
}
// x509
else if (info.useX509Cert() && principal instanceof X500Principal
|| principal instanceof X509CertPath) {
// Extract common name
String x509CommonName = EndpointContextUtil.extractCN(principal.getName());
if (x509CommonName.equals(info.getEndpoint())) {
return true;
}
}
}
}
}
return false;
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.eclipse.californium.core.network.Endpoint;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.leshan.core.californium.CoapResponseCallback;
import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
Expand Down Expand Up @@ -68,8 +69,10 @@
import org.eclipse.leshan.server.registration.RegistrationUpdate;
import org.eclipse.leshan.server.request.LwM2mRequestSender;
import org.eclipse.leshan.server.security.Authorizer;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.eclipse.leshan.server.security.SecurityStore;
import org.eclipse.leshan.server.security.SecurityStoreListener;
import org.eclipse.leshan.util.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -175,6 +178,9 @@ public LeshanServer(CoapEndpoint unsecuredEndpoint, CoapEndpoint securedEndpoint
requestSender = createRequestSender(securedEndpoint, unsecuredEndpoint, registrationService, observationService,
this.modelProvider, encoder, decoder, presenceService);

// connection cleaner
createConnectionCleaner(securityStore, securedEndpoint);

coapApi = new CoapAPI();
}

Expand Down Expand Up @@ -262,6 +268,24 @@ public void registered(Registration registration, Registration previousReg,
return requestSender;
}

protected void createConnectionCleaner(SecurityStore securityStore, CoapEndpoint securedEndpoint) {
if (securedEndpoint != null && securedEndpoint.getConnector() instanceof DTLSConnector
&& securityStore instanceof EditableSecurityStore) {

final ConnectionCleaner connectionCleaner = new ConnectionCleaner(
(DTLSConnector) securedEndpoint.getConnector());

((EditableSecurityStore) securityStore).setListener(new SecurityStoreListener() {
@Override
public void securityInfoRemoved(boolean infosAreCompromised, SecurityInfo... infos) {
if (infosAreCompromised) {
connectionCleaner.cleanConnectionFor(infos);
}
}
});
}
}

/**
* Starts the server and binds it to the specified port.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,16 @@ public interface EditableSecurityStore extends SecurityStore {
* Removes the security information for a given end-point.
*
* @param endpoint the client end-point
* @param infosAreCompromised if the {@link SecurityInfo} removed should be considered as compromised and so must
* not be used anymore immediately.
* @return the removed {@link SecurityInfo} or <code>null</code> if no info for the end-point.
*/
SecurityInfo remove(String endpoint);
SecurityInfo remove(String endpoint, boolean infosAreCompromised);

/**
* Set a Listener for this store.
*
* @param listener the security store listener
*/
void setListener(SecurityStoreListener listener);
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException
}

@Override
public SecurityInfo remove(String endpoint) {
public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {
writeLock.lock();
try {
SecurityInfo info = super.remove(endpoint);
SecurityInfo info = super.remove(endpoint, infosAreCompromised);
if (info != null) {
saveToFile();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public class InMemorySecurityStore implements EditableSecurityStore {
// by PSK identity
protected Map<String, SecurityInfo> securityByIdentity = new HashMap<>();

private SecurityStoreListener listener;

public InMemorySecurityStore() {
}

Expand Down Expand Up @@ -106,7 +108,7 @@ public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException
}

@Override
public SecurityInfo remove(String endpoint) {
public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {
writeLock.lock();
try {
SecurityInfo info = securityByEp.get(endpoint);
Expand All @@ -115,10 +117,18 @@ public SecurityInfo remove(String endpoint) {
securityByIdentity.remove(info.getIdentity());
}
securityByEp.remove(endpoint);
if (listener != null) {
listener.securityInfoRemoved(infosAreCompromised, info);
}
}
return info;
} finally {
writeLock.unlock();
}
}

@Override
public void setListener(SecurityStoreListener listener) {
this.listener = listener;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2020 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
*******************************************************************************/
package org.eclipse.leshan.server.security;

/**
* A Listener for {@link SecurityStore}
*/
public interface SecurityStoreListener {
/**
* Called when {@link SecurityInfo} are removed.
*
* @param infosAreCompromised True if info are compromised and should not be used immediately
* @param infos Array of removed {@link SecurityInfo}
*/
void securityInfoRemoved(boolean infosAreCompromised, SecurityInfo... infos);
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws
}

LOG.debug("Removing security info for end-point {}", endpoint);
if (this.store.remove(endpoint) != null) {
if (this.store.remove(endpoint, true) != null) {
resp.sendError(HttpServletResponse.SC_OK);
} else {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.eclipse.leshan.server.security.SecurityStore;
import org.eclipse.leshan.server.security.SecurityStoreListener;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.ScanParams;
Expand All @@ -41,6 +42,7 @@ public class RedisSecurityStore implements EditableSecurityStore {
private static final String PSKID_SEC = "PSKID#SEC";

private final Pool<Jedis> pool;
private SecurityStoreListener listener;

public RedisSecurityStore(Pool<Jedis> pool) {
this.pool = pool;
Expand Down Expand Up @@ -118,7 +120,7 @@ public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException
}

@Override
public SecurityInfo remove(String endpoint) {
public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {
try (Jedis j = pool.getResource()) {
byte[] data = j.get((SEC_EP + endpoint).getBytes());

Expand All @@ -128,6 +130,9 @@ public SecurityInfo remove(String endpoint) {
j.hdel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
}
j.del((SEC_EP + endpoint).getBytes());
if (listener != null) {
listener.securityInfoRemoved(infosAreCompromised, info);
}
return info;
}
}
Expand All @@ -141,4 +146,9 @@ private byte[] serialize(SecurityInfo secInfo) {
private SecurityInfo deserialize(byte[] data) {
return SecurityInfoSerDes.deserialize(data);
}

@Override
public void setListener(SecurityStoreListener listener) {
this.listener = listener;
}
}

0 comments on commit 57bbbeb

Please sign in to comment.