2 * Copyright (c) 2016 Brocade Communication Systems and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.callhome.mount;
10 import com.google.common.base.Objects;
11 import com.google.common.net.InetAddresses;
12 import java.net.InetSocketAddress;
13 import java.net.SocketAddress;
14 import java.security.PublicKey;
15 import java.util.Collection;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.ConcurrentMap;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
20 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
21 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
22 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.netconf.callhome.protocol.AuthorizedKeysDecoder;
25 import org.opendaylight.netconf.callhome.protocol.CallHomeAuthorization;
26 import org.opendaylight.netconf.callhome.protocol.CallHomeAuthorization.Builder;
27 import org.opendaylight.netconf.callhome.protocol.CallHomeAuthorizationProvider;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.NetconfCallhomeServer;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.credentials.Credentials;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.AllowedDevices;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.Global;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.Device;
33 import org.opendaylight.yangtools.concepts.ListenerRegistration;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider, AutoCloseable {
40 private static final Logger LOG = LoggerFactory.getLogger(CallHomeAuthProviderImpl.class);
41 private static final InstanceIdentifier<Global> GLOBAL_PATH =
42 InstanceIdentifier.create(NetconfCallhomeServer.class).child(Global.class);
43 private static final DataTreeIdentifier<Global> GLOBAL =
44 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, GLOBAL_PATH);
46 private static final InstanceIdentifier<Device> ALLOWED_DEVICES_PATH =
47 InstanceIdentifier.create(NetconfCallhomeServer.class).child(AllowedDevices.class).child(Device.class);
48 private static final DataTreeIdentifier<Device> ALLOWED_DEVICES =
49 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, ALLOWED_DEVICES_PATH);
51 private final GlobalConfig globalConfig = new GlobalConfig();
52 private final DeviceConfig deviceConfig = new DeviceConfig();
53 private final ListenerRegistration<GlobalConfig> configReg;
54 private final ListenerRegistration<DeviceConfig> deviceReg;
56 public CallHomeAuthProviderImpl(DataBroker broker) {
57 configReg = broker.registerDataTreeChangeListener(GLOBAL, globalConfig);
58 deviceReg = broker.registerDataTreeChangeListener(ALLOWED_DEVICES, deviceConfig);
62 public CallHomeAuthorization provideAuth(SocketAddress remoteAddress, PublicKey serverKey) {
63 Device deviceSpecific = deviceConfig.get(serverKey);
65 Credentials deviceCred;
66 if (deviceSpecific != null) {
67 sessionName = deviceSpecific.getUniqueId();
68 deviceCred = deviceSpecific.getCredentials();
69 } else if (globalConfig.allowedUnknownKeys()) {
70 sessionName = fromRemoteAddress(remoteAddress);
73 return CallHomeAuthorization.rejected();
75 final Credentials credentials = deviceCred != null ? deviceCred : globalConfig.getCredentials();
77 if (credentials == null) {
78 LOG.info("No credentials found for {}, rejecting.", remoteAddress);
79 return CallHomeAuthorization.rejected();
81 Builder authBuilder = CallHomeAuthorization.serverAccepted(sessionName, credentials.getUsername());
82 for (String password : credentials.getPasswords()) {
83 authBuilder.addPassword(password);
85 return authBuilder.build();
89 public void close() throws Exception {
94 private String fromRemoteAddress(SocketAddress remoteAddress) {
95 if (remoteAddress instanceof InetSocketAddress) {
96 InetSocketAddress socketAddress = (InetSocketAddress) remoteAddress;
97 return InetAddresses.toAddrString(socketAddress.getAddress()) + ":" + socketAddress.getPort();
99 return remoteAddress.toString();
103 private class DeviceConfig implements DataTreeChangeListener<Device> {
105 private ConcurrentMap<PublicKey, Device> byPublicKey = new ConcurrentHashMap<PublicKey, Device>();
108 public void onDataTreeChanged(Collection<DataTreeModification<Device>> arg0) {
109 for (DataTreeModification<Device> dataTreeModification : arg0) {
110 DataObjectModification<Device> rootNode = dataTreeModification.getRootNode();
115 private void process(DataObjectModification<Device> deviceMod) {
116 Device before = deviceMod.getDataBefore();
117 Device after = deviceMod.getDataAfter();
119 if (before == null) {
121 } else if (after == null) {
123 removeDevice(before);
125 if (!Objects.equal(before.getSshHostKey(), after.getSshHostKey())) {
126 // key changed // we should remove previous key.
127 removeDevice(before);
133 private void putDevice(Device device) {
134 PublicKey key = publicKey(device);
138 byPublicKey.put(key, device);
141 private void removeDevice(Device device) {
142 PublicKey key = publicKey(device);
146 byPublicKey.remove(key);
149 private PublicKey publicKey(Device device) {
150 String hostKey = device.getSshHostKey();
152 return new AuthorizedKeysDecoder().decodePublicKey(hostKey);
153 } catch (Exception e) {
154 LOG.error("Unable to decode SSH key for {}. Ignoring update for this device",device.getUniqueId(),e);
159 private Device get(PublicKey key) {
160 return byPublicKey.get(key);
164 private class GlobalConfig implements DataTreeChangeListener<Global> {
167 private volatile Global current = null;
170 public void onDataTreeChanged(Collection<DataTreeModification<Global>> arg0) {
171 for (DataTreeModification<Global> dataTreeModification : arg0) {
172 current = dataTreeModification.getRootNode().getDataAfter();
176 boolean allowedUnknownKeys() {
177 if (current == null) {
180 // Deal with null values.
181 return Boolean.TRUE.equals(current.isAcceptAllSshKeys());
184 Credentials getCredentials() {
185 return current != null ? current.getCredentials() : null;