Commit 38af4d5d2da99804c2aebe829e9c6aeb9b786701

Authored by Andrii Shvaika
1 parent 7322afac

Implementation of custom L2M2M Authorizer

... ... @@ -15,14 +15,53 @@
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.secure;
17 17
  18 +import lombok.RequiredArgsConstructor;
18 19 import org.eclipse.leshan.core.request.Identity;
19 20 import org.eclipse.leshan.core.request.UplinkRequest;
20 21 import org.eclipse.leshan.server.registration.Registration;
21 22 import org.eclipse.leshan.server.security.Authorizer;
  23 +import org.eclipse.leshan.server.security.SecurityChecker;
  24 +import org.eclipse.leshan.server.security.SecurityInfo;
  25 +import org.springframework.stereotype.Component;
  26 +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
  27 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
  28 +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
  29 +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mSecurityStore;
22 30
  31 +@Component
  32 +@RequiredArgsConstructor
  33 +@TbLwM2mTransportComponent
23 34 public class TbLwM2MAuthorizer implements Authorizer {
  35 +
  36 + private final TbLwM2MDtlsSessionStore sessionStorage;
  37 + private final TbLwM2mSecurityStore securityStore;
  38 + private final SecurityChecker securityChecker = new SecurityChecker();
  39 + private final LwM2mClientContext clientContext;
  40 +
24 41 @Override
25 42 public Registration isAuthorized(UplinkRequest<?> request, Registration registration, Identity senderIdentity) {
26   - return null;
  43 + if (senderIdentity.isX509()) {
  44 + TbX509DtlsSessionInfo sessionInfo = sessionStorage.get(registration.getEndpoint());
  45 + if (sessionInfo != null) {
  46 + if (senderIdentity.getX509CommonName().equals(sessionInfo.getX509CommonName())) {
  47 + clientContext.registerClient(registration, sessionInfo.getCredentials());
  48 + // X509 certificate is valid and matches endpoint.
  49 + return registration;
  50 + } else {
  51 + // X509 certificate is not valid.
  52 + return null;
  53 + }
  54 + }
  55 + // If session info is not found, this may be the trusted certificate, so we still need to check all other options below.
  56 + }
  57 + SecurityInfo expectedSecurityInfo = null;
  58 + if (securityStore != null) {
  59 + expectedSecurityInfo = securityStore.getByEndpoint(registration.getEndpoint());
  60 + }
  61 + if (securityChecker.checkSecurityInfo(registration.getEndpoint(), senderIdentity, expectedSecurityInfo)) {
  62 + return registration;
  63 + } else {
  64 + return null;
  65 + }
27 66 }
28 67 }
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.secure;
17 17
  18 +import com.fasterxml.jackson.databind.JsonNode;
18 19 import lombok.RequiredArgsConstructor;
19 20 import lombok.extern.slf4j.Slf4j;
20 21 import org.eclipse.californium.elements.util.CertPathUtil;
... ... @@ -32,6 +33,7 @@ import org.eclipse.californium.scandium.util.ServerNames;
32 33 import org.springframework.beans.factory.annotation.Value;
33 34 import org.springframework.stereotype.Component;
34 35 import org.springframework.util.StringUtils;
  36 +import org.thingsboard.common.util.JacksonUtil;
35 37 import org.thingsboard.server.common.data.DeviceProfile;
36 38 import org.thingsboard.server.common.msg.EncryptionUtil;
37 39 import org.thingsboard.server.common.transport.TransportService;
... ... @@ -40,6 +42,7 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes
40 42 import org.thingsboard.server.common.transport.util.SslUtil;
41 43 import org.thingsboard.server.gen.transport.TransportProtos;
42 44 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
  45 +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
43 46
44 47 import javax.annotation.PostConstruct;
45 48 import javax.security.auth.x500.X500Principal;
... ... @@ -60,7 +63,7 @@ import java.util.concurrent.TimeUnit;
60 63 public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVerifier {
61 64
62 65 private final TransportService transportService;
63   - private final TbLwM2MDtlsSessionStorage sessionStorage;
  66 + private final TbLwM2MDtlsSessionStore sessionStorage;
64 67 private final LwM2MTransportServerConfig config;
65 68
66 69 @SuppressWarnings("deprecation")
... ... @@ -130,16 +133,24 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
130 133 latch.countDown();
131 134 }
132 135 });
133   - latch.await(10, TimeUnit.SECONDS);
134   - ValidateDeviceCredentialsResponse msg = deviceCredentialsResponse[0];
135   - if (msg != null && strCert.equals(msg.getCredentials())) {
136   - credentialsBody = msg.getCredentials();
137   - DeviceProfile deviceProfile = msg.getDeviceProfile();
138   - if (msg.hasDeviceInfo() && deviceProfile != null) {
139   - String endpoint = sha3Hash; //TODO: extract endpoint from credentials body and push to storage
140   - sessionStorage.put(endpoint, msg);
  136 + if (latch.await(10, TimeUnit.SECONDS)) {
  137 + ValidateDeviceCredentialsResponse msg = deviceCredentialsResponse[0];
  138 + if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
  139 + JsonNode credentialsJson = JacksonUtil.toJsonNode(msg.getCredentials());
  140 + String certBody = credentialsJson.get("cert").asText();
  141 + String endpoint = credentialsJson.get("endpoint").asText();
  142 + if (strCert.equals(certBody)) {
  143 + //TODO: extract endpoint from credentials body and push to storage
  144 + credentialsBody = msg.getCredentials();
  145 + DeviceProfile deviceProfile = msg.getDeviceProfile();
  146 + if (msg.hasDeviceInfo() && deviceProfile != null) {
  147 + sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg));
  148 + break;
  149 + }
  150 + } else {
  151 + log.trace("[{}][{}] Certificate mismatch. Expected: {}, Actual: {}", endpoint, sha3Hash, strCert, certBody);
  152 + }
141 153 }
142   - break;
143 154 }
144 155 } catch (InterruptedException |
145 156 CertificateEncodingException |
... ...
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbX509DtlsSessionInfo.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsSessionStorage.java
... ... @@ -15,12 +15,13 @@
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.secure;
17 17
  18 +import lombok.Data;
18 19 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
19 20
20   -public interface TbLwM2MDtlsSessionStorage {
  21 +@Data
  22 +public class TbX509DtlsSessionInfo {
21 23
22   - void put(String endpoint, ValidateDeviceCredentialsResponse msg);
23   -
24   - ValidateDeviceCredentialsResponse get(String endpoint);
  24 + private final String x509CommonName;
  25 + private final ValidateDeviceCredentialsResponse credentials;
25 26
26 27 }
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.client;
17 17
18 18 import org.eclipse.leshan.server.registration.Registration;
19 19 import org.thingsboard.server.common.data.DeviceProfile;
  20 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
20 21 import org.thingsboard.server.gen.transport.TransportProtos;
21 22
22 23 import java.util.Collection;
... ... @@ -59,4 +60,6 @@ public interface LwM2mClientContext {
59 60 Set<String> getSupportedIdVerInClient(Registration registration);
60 61
61 62 LwM2mClient getClientByDeviceId(UUID deviceId);
  63 +
  64 + void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials);
62 65 }
... ...
... ... @@ -21,6 +21,7 @@ import org.eclipse.leshan.server.registration.Registration;
21 21 import org.eclipse.leshan.server.security.EditableSecurityStore;
22 22 import org.springframework.stereotype.Service;
23 23 import org.thingsboard.server.common.data.DeviceProfile;
  24 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
24 25 import org.thingsboard.server.gen.transport.TransportProtos;
25 26 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
26 27 import org.thingsboard.server.transport.lwm2m.secure.EndpointSecurityInfo;
... ... @@ -137,6 +138,13 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
137 138 }
138 139
139 140 @Override
  141 + public void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials) {
  142 + LwM2mClient client = new LwM2mClient(context.getNodeId(), registration.getEndpoint(), null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID());
  143 + lwM2mClientsByEndpoint.put(registration.getEndpoint(), client);
  144 + lwM2mClientsByRegistrationId.put(registration.getId(), client);
  145 + }
  146 +
  147 + @Override
140 148 public Collection<LwM2mClient> getLwM2mClients() {
141 149 return lwM2mClientsByEndpoint.values();
142 150 }
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.store;
  17 +
  18 +import org.springframework.stereotype.Component;
  19 +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
  20 +import org.thingsboard.server.transport.lwm2m.secure.TbX509DtlsSessionInfo;
  21 +
  22 +import java.util.concurrent.ConcurrentHashMap;
  23 +
  24 +@Component
  25 +@TbLwM2mTransportComponent
  26 +public class TbL2M2MDtlsSessionInMemoryStore implements TbLwM2MDtlsSessionStore {
  27 +
  28 + private final ConcurrentHashMap<String, TbX509DtlsSessionInfo> store = new ConcurrentHashMap<>();
  29 +
  30 + @Override
  31 + public void put(String endpoint, TbX509DtlsSessionInfo msg) {
  32 + store.put(endpoint, msg);
  33 + }
  34 +
  35 + @Override
  36 + public TbX509DtlsSessionInfo get(String endpoint) {
  37 + return store.get(endpoint);
  38 + }
  39 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.store;
  17 +
  18 +
  19 +import org.thingsboard.server.transport.lwm2m.secure.TbX509DtlsSessionInfo;
  20 +
  21 +public interface TbLwM2MDtlsSessionStore {
  22 +
  23 + void put(String endpoint, TbX509DtlsSessionInfo msg);
  24 +
  25 + TbX509DtlsSessionInfo get(String endpoint);
  26 +
  27 + //TODO: add way to delete the session by endpoint.
  28 +
  29 +}
... ...
... ... @@ -20,12 +20,16 @@ import org.eclipse.leshan.server.security.EditableSecurityStore;
20 20 import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
21 21 import org.eclipse.leshan.server.security.SecurityInfo;
22 22 import org.eclipse.leshan.server.security.SecurityStoreListener;
  23 +import org.springframework.stereotype.Component;
  24 +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
23 25 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
24 26 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
25 27
26 28 import java.util.Collection;
27 29
28 30 @Slf4j
  31 +@Component
  32 +@TbLwM2mTransportComponent
29 33 public class TbLwM2mSecurityStore implements EditableSecurityStore {
30 34
31 35 private final LwM2mClientContext clientContext;
... ...