2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-09-08 18:15:09 +00:00
Files
kdeconnect-android/src/org/kde/kdeconnect/Plugins/SftpPlugin/SimpleSftpServer.java

207 lines
6.7 KiB
Java
Raw Normal View History

2014-11-16 23:14:06 -08:00
/*
2020-08-17 16:17:20 +02:00
* SPDX-FileCopyrightText: 2014 Samoilenko Yuri <kinnalru@gmail.com>
2014-11-16 23:14:06 -08:00
*
2020-08-17 16:17:20 +02:00
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
2018-12-25 00:10:58 +01:00
*/
2014-11-16 23:14:06 -08:00
package org.kde.kdeconnect.Plugins.SftpPlugin;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import org.apache.sshd.SshServer;
import org.apache.sshd.common.file.nativefs.NativeFileSystemFactory;
import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
import org.apache.sshd.common.signature.SignatureDSA;
import org.apache.sshd.common.signature.SignatureECDSA;
import org.apache.sshd.common.signature.SignatureRSA;
import org.apache.sshd.common.util.SecurityUtils;
import org.apache.sshd.server.PasswordAuthenticator;
2014-01-16 09:51:32 +04:00
import org.apache.sshd.server.PublickeyAuthenticator;
import org.apache.sshd.server.command.ScpCommandFactory;
2016-06-09 13:42:15 +02:00
import org.apache.sshd.server.kex.DHG14;
import org.apache.sshd.server.kex.ECDHP256;
import org.apache.sshd.server.kex.ECDHP384;
import org.apache.sshd.server.kex.ECDHP521;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.sftp.SftpSubsystem;
2014-01-16 09:51:32 +04:00
import org.kde.kdeconnect.Device;
2016-02-17 04:48:01 -08:00
import org.kde.kdeconnect.Helpers.RandomHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
2014-01-16 09:51:32 +04:00
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Collections;
Use Storage Access Framework on SDK >= 21 (Lollipop and above) Summary: Use Storage Access Framework on Android running SDK >= 21 so writing to sdcard will work again |{F6546802}|{F6546803}|{F6546804}| |API 21+|API 19-|Edit| Test Plan: Install patch on Android phone with Build.Version < 19 (Kitkat) - Without a sdcard: Verify that dolphin displays an "All Files" entry that is empty - With a sdcard and with "Add camera folder shortcut" turned off: Verify that dolphin displays the configured display name of the sdcard - With a sdcard and with "Add camera folder shortcut" turned on: Verify that dolphin displays the configured display name of the sdcard and also lists a "Camera pictures" shortcut - With a sdcard: Verify that when changing the display name or the "Add camera folder shortcut" preference dolphin displays the updated items (after pressing F5) - With a sdcard: Verify that files can be read and written to/from the sdcard Install patch on Android phone with Build.Version < 19 (Kitkat) - Repeat the above tests except for the read/write test: Verify that files can be read from the sdcard Install patch on Android phone with Build.Version > 21 (Lollipop) - Without any configured storage locations: Verify dolphin displays an "All Files" entry that is empty - With configured storage locations: Verify dolphin displays the display names of the configured storage locations and that entering a location displays the correct directory entries - Make one or several changes to the configured storage locations: Verify dolphin displays the display names of the configured storage locations (after pressing F5) and that entering a location displays the correct directory entries Reviewers: #kde_connect, albertvaka, sredman Reviewed By: #kde_connect, albertvaka, sredman Subscribers: albertvaka, sredman, kdeconnect Tags: #kde_connect Differential Revision: https://phabricator.kde.org/D18212
2019-03-08 13:44:54 +01:00
import java.util.List;
import static org.kde.kdeconnect.Helpers.SecurityHelpers.ConstantTimeCompareKt.constantTimeCompare;
class SimpleSftpServer {
private static final int STARTPORT = 1739;
private static final int ENDPORT = 1764;
static final String USER = "kdeconnect";
private int port = -1;
private boolean started = false;
private final SimplePasswordAuthenticator passwordAuth = new SimplePasswordAuthenticator();
private final SimplePublicKeyAuthenticator keyAuth = new SimplePublicKeyAuthenticator();
static {
SecurityUtils.setRegisterBouncyCastle(false);
}
2023-03-19 11:56:25 +01:00
boolean initialized = false;
private final SshServer sshd = SshServer.setUpDefaultServer();
private AndroidFileSystemFactory safFileSystemFactory;
public void setSafRoots(List<SftpPlugin.StorageInfo> storageInfoList) {
safFileSystemFactory.initRoots(storageInfoList);
}
2023-03-19 11:56:25 +01:00
void initialize(Context context, Device device) throws GeneralSecurityException {
sshd.setSignatureFactories(Arrays.asList(
new SignatureECDSA.NISTP256Factory(),
new SignatureECDSA.NISTP384Factory(),
new SignatureECDSA.NISTP521Factory(),
new SignatureDSA.Factory(),
new SignatureRSASHA256.Factory(),
new SignatureRSA.Factory() // Insecure SHA1, left for backwards compatibility
));
sshd.setKeyExchangeFactories(Arrays.asList(
new ECDHP256.Factory(), // ecdh-sha2-nistp256
new ECDHP384.Factory(), // ecdh-sha2-nistp384
new ECDHP521.Factory(), // ecdh-sha2-nistp521
new DHG14_256.Factory(), // diffie-hellman-group14-sha256
new DHG14.Factory() // Insecure diffie-hellman-group14-sha1, left for backwards-compatibility.
));
//Reuse this device keys for the ssh connection as well
2018-08-31 21:12:04 +02:00
final KeyPair keyPair;
PrivateKey privateKey = RsaHelper.getPrivateKey(context);
PublicKey publicKey = RsaHelper.getPublicKey(context);
keyPair = new KeyPair(publicKey, privateKey);
sshd.setKeyPairProvider(new AbstractKeyPairProvider() {
@Override
2018-08-31 21:12:04 +02:00
public Iterable<KeyPair> loadKeys() {
return Collections.singletonList(keyPair);
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
sshd.setFileSystemFactory(new NativeFileSystemFactory());
} else {
safFileSystemFactory = new AndroidFileSystemFactory(context);
sshd.setFileSystemFactory(safFileSystemFactory);
}
sshd.setCommandFactory(new ScpCommandFactory());
2018-08-31 21:12:04 +02:00
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystem.Factory()));
keyAuth.deviceKey = device.getCertificate().getPublicKey();
sshd.setPublickeyAuthenticator(keyAuth);
sshd.setPasswordAuthenticator(passwordAuth);
2023-03-19 11:56:25 +01:00
initialized = true;
}
public boolean start() {
if (!started) {
regeneratePassword();
port = STARTPORT;
while (!started) {
try {
sshd.setPort(port);
sshd.start();
started = true;
} catch (IOException e) {
port++;
if (port >= ENDPORT) {
port = -1;
Log.e("SftpServer", "No more ports available");
return false;
}
}
}
}
return true;
}
public void stop() {
try {
started = false;
sshd.stop(true);
} catch (Exception e) {
2019-03-31 20:09:44 +02:00
Log.e("SFTP", "Exception while stopping the server", e);
}
}
Use Storage Access Framework on SDK >= 21 (Lollipop and above) Summary: Use Storage Access Framework on Android running SDK >= 21 so writing to sdcard will work again |{F6546802}|{F6546803}|{F6546804}| |API 21+|API 19-|Edit| Test Plan: Install patch on Android phone with Build.Version < 19 (Kitkat) - Without a sdcard: Verify that dolphin displays an "All Files" entry that is empty - With a sdcard and with "Add camera folder shortcut" turned off: Verify that dolphin displays the configured display name of the sdcard - With a sdcard and with "Add camera folder shortcut" turned on: Verify that dolphin displays the configured display name of the sdcard and also lists a "Camera pictures" shortcut - With a sdcard: Verify that when changing the display name or the "Add camera folder shortcut" preference dolphin displays the updated items (after pressing F5) - With a sdcard: Verify that files can be read and written to/from the sdcard Install patch on Android phone with Build.Version < 19 (Kitkat) - Repeat the above tests except for the read/write test: Verify that files can be read from the sdcard Install patch on Android phone with Build.Version > 21 (Lollipop) - Without any configured storage locations: Verify dolphin displays an "All Files" entry that is empty - With configured storage locations: Verify dolphin displays the display names of the configured storage locations and that entering a location displays the correct directory entries - Make one or several changes to the configured storage locations: Verify dolphin displays the display names of the configured storage locations (after pressing F5) and that entering a location displays the correct directory entries Reviewers: #kde_connect, albertvaka, sredman Reviewed By: #kde_connect, albertvaka, sredman Subscribers: albertvaka, sredman, kdeconnect Tags: #kde_connect Differential Revision: https://phabricator.kde.org/D18212
2019-03-08 13:44:54 +01:00
public boolean isStarted() {
return started;
}
String regeneratePassword() {
String password = RandomHelper.randomString(28);
passwordAuth.setPassword(password);
return password;
}
2018-12-25 00:09:52 +01:00
int getPort() {
return port;
}
2023-03-19 11:56:25 +01:00
public boolean isInitialized() {
return initialized;
}
static class SimplePasswordAuthenticator implements PasswordAuthenticator {
MessageDigest sha;
{
try {
sha = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public void setPassword(String password) {
sha.digest(password.getBytes(StandardCharsets.UTF_8));
}
byte[] passwordHash;
@Override
public boolean authenticate(String user, String password, ServerSession session) {
byte[] receivedPasswordHash = sha.digest(password.getBytes(StandardCharsets.UTF_8));
return user.equals(SimpleSftpServer.USER) && constantTimeCompare(passwordHash, receivedPasswordHash);
}
}
static class SimplePublicKeyAuthenticator implements PublickeyAuthenticator {
2018-10-26 23:53:58 +02:00
PublicKey deviceKey;
@Override
public boolean authenticate(String user, PublicKey key, ServerSession session) {
return deviceKey.equals(key);
}
}
}