blob: 56151129d10f7e225073d01026100686fefb2b0a [file] [log] [blame]
/*
* Copyright 2019-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.ofoverlay.impl.util;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.KeyPair;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import org.onlab.packet.IpAddress;
import org.slf4j.Logger;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Class for SSH key exchange.
*/
public class SshkeyExchange {
protected static final Logger log = getLogger(SshkeyExchange.class);
private static final String HOME_ENV = System.getProperty("user.home");
private static final String USER_ENV = System.getProperty("user.name");
private static final String PUBLIC_KEY = "/.ssh/id_rsa.pub";
private static final String PRIVATE_KEY = "/.ssh/id_rsa";
private static final String SFTP_CHANNEL = "sftp";
private static final String SSH_HOME = "/.ssh/";
private static final String SSH_AUTH_KEY = "/.ssh/authorized_keys";
private static final int SFTPPORT = 22;
private static final int DIR_PER = 448;
private static final int FILE_PER = 384;
private static final int KEY_SIZE = 2048;
private static final int DEFAULT_SESSION_TIMEOUT = 30000; // milliseconds
private Session getJschSession(String user, String host, String password) {
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
Session session;
try {
session = new JSch().getSession(user, host, SFTPPORT);
session.setPassword(password);
session.setConfig(config);
} catch (JSchException e) {
log.error("Exception in getJschSession", e);
return null;
}
return session;
}
private boolean generateKeyPair() {
KeyPair kpair;
StringBuilder command = new StringBuilder()
.append("chmod 600 ")
.append(HOME_ENV)
.append(PRIVATE_KEY);
try {
kpair = KeyPair.genKeyPair(new JSch(), KeyPair.RSA, KEY_SIZE);
kpair.writePrivateKey(HOME_ENV + PRIVATE_KEY);
kpair.writePublicKey(HOME_ENV + PUBLIC_KEY, USER_ENV);
Runtime.getRuntime().exec(command.toString());
kpair.dispose();
} catch (JSchException | IOException e) {
log.error("Exception in generateKeyPair", e);
return false;
}
return true;
}
/**
* Exchanges SSH key.
* @param host SSH server host
* @param user user
* @param password password
* @return SSH key exchange success or not
*/
public boolean sshAutoKeyExchange(IpAddress host, String user, String password) {
Session session = getJschSession(user, host.toString(), password);
boolean returnFlag;
File publickeyPath = new File(HOME_ENV + PUBLIC_KEY);
if (session == null) {
log.error("Error While establishing SFTP connection with {}", host.toString());
return false;
}
Channel channel;
String remoteHome;
ChannelSftp sftp;
FileInputStream fis = null;
SftpATTRS attrs = null;
try {
session.connect(DEFAULT_SESSION_TIMEOUT);
channel = session.openChannel(SFTP_CHANNEL);
if (channel == null) {
log.error("SFTP channel open failed for {}", host.toString());
return false;
}
channel.connect();
sftp = (ChannelSftp) channel;
remoteHome = sftp.getHome();
// checking key pair existance
if (!publickeyPath.exists()) {
File dirs = new File(HOME_ENV + SSH_HOME);
if (!dirs.exists() && !dirs.mkdirs()) {
log.error("{} not exists and unable to create ", dirs.getPath());
return false;
} else if (!generateKeyPair()) {
log.error("SSH Key pair generation failed");
return false;
}
}
// checking for authenticate_keys file existance
fis = new FileInputStream(publickeyPath);
try {
sftp.lstat(remoteHome + SSH_HOME);
} catch (SftpException e) {
sftp.mkdir(remoteHome + SSH_HOME);
sftp.chmod(700, remoteHome + SSH_HOME);
}
try {
attrs = sftp.lstat(remoteHome + SSH_AUTH_KEY);
} catch (SftpException e) {
log.info("authorized_keys file does not exist at remote device ,"
+ "a new file will be created");
}
if (attrs != null) {
sftp.get(remoteHome + SSH_AUTH_KEY, HOME_ENV + "/tempauthorized_keys");
String pubKey;
try (Stream<String> st = Files.lines(Paths.get(HOME_ENV + PUBLIC_KEY))) {
pubKey = st.collect(Collectors.joining());
}
String authKey;
try (Stream<String> st = Files.lines(Paths.get(HOME_ENV + "/tempauthorized_keys"))) {
authKey = st.collect(Collectors.joining());
}
if (authKey.contains(pubKey)) {
log.info("Skipping key append to server as Key is already added");
} else {
sftp.put(fis, remoteHome + SSH_AUTH_KEY, ChannelSftp.APPEND);
log.info("Public key appended to server");
}
} else {
sftp.put(fis, remoteHome + SSH_AUTH_KEY, ChannelSftp.APPEND);
// Give proper permission to file and directory.
sftp.chmod(DIR_PER, remoteHome + SSH_HOME);
sftp.chmod(FILE_PER, remoteHome + SSH_AUTH_KEY);
log.info("Public key appended to server");
}
sftp.exit();
session.disconnect();
returnFlag = true;
} catch (JSchException | SftpException | IOException e) {
log.error("Exception occured because of {} ", e);
returnFlag = false;
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
log.info("Error closing public key file");
}
}
}
return returnFlag;
}
}