From d41034ad2ff334707a58ace5f3cd1de5fcfa1815 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 18 Sep 2015 08:30:29 -0400 Subject: [PATCH] Update to SSHD 1.0.0 --- .classpath | 2 +- build.moxie | 2 +- gitblit.iml | 6 +- .../ssh/DisabledFilesystemFactory.java | 35 ++-- .../transport/ssh/FileKeyPairProvider.java | 154 ++++++++++++++++++ .../transport/ssh/NonForwardingFilter.java | 7 +- .../com/gitblit/transport/ssh/SshDaemon.java | 23 ++- .../transport/ssh/SshDaemonClient.java | 2 +- .../com/gitblit/transport/ssh/SshKey.java | 7 +- .../transport/ssh/SshKeyAuthenticator.java | 2 +- .../transport/ssh/SshServerSession.java | 4 +- .../ssh/SshServerSessionFactory.java | 2 +- .../ssh/UsernamePasswordAuthenticator.java | 2 +- .../java/com/gitblit/tests/SshDaemonTest.java | 4 +- .../java/com/gitblit/tests/SshUnitTest.java | 12 +- 15 files changed, 205 insertions(+), 59 deletions(-) create mode 100644 src/main/java/com/gitblit/transport/ssh/FileKeyPairProvider.java diff --git a/.classpath b/.classpath index 1c01cdff..a04c9e2e 100644 --- a/.classpath +++ b/.classpath @@ -52,7 +52,7 @@ - + diff --git a/build.moxie b/build.moxie index 845a7a34..3958df8e 100644 --- a/build.moxie +++ b/build.moxie @@ -111,7 +111,7 @@ properties: { bouncycastle.version : 1.52 selenium.version : 2.28.0 wikitext.version : 1.4 - sshd.version: 0.14.0 + sshd.version: 1.0.0 mina.version: 2.0.9 guice.version : 4.0-beta5 # Gitblit maintains a fork of guice-servlet diff --git a/gitblit.iml b/gitblit.iml index 9f896d10..d6982f35 100644 --- a/gitblit.iml +++ b/gitblit.iml @@ -527,13 +527,13 @@ - + - + - + diff --git a/src/main/java/com/gitblit/transport/ssh/DisabledFilesystemFactory.java b/src/main/java/com/gitblit/transport/ssh/DisabledFilesystemFactory.java index de661a4d..9bab3b8e 100644 --- a/src/main/java/com/gitblit/transport/ssh/DisabledFilesystemFactory.java +++ b/src/main/java/com/gitblit/transport/ssh/DisabledFilesystemFactory.java @@ -16,31 +16,22 @@ package com.gitblit.transport.ssh; import java.io.IOException; +import java.nio.file.FileSystem; -import org.apache.sshd.common.Session; import org.apache.sshd.common.file.FileSystemFactory; -import org.apache.sshd.common.file.FileSystemView; -import org.apache.sshd.common.file.SshFile; +import org.apache.sshd.common.session.Session; public class DisabledFilesystemFactory implements FileSystemFactory { - @Override - public FileSystemView createFileSystemView(Session session) throws IOException { - return new FileSystemView() { - @Override - public SshFile getFile(SshFile baseDir, String file) { - return null; - } - - @Override - public SshFile getFile(String file) { - return null; - } - - @Override - public FileSystemView getNormalizedView() { - return null; - } - }; - } + /** + * Create user specific file system. + * + * @param session The session created for the user + * @return The current {@link FileSystem} for the provided session + * @throws java.io.IOException when the filesystem can not be created + */ + @Override + public FileSystem createFileSystem(Session session) throws IOException { + return null; + } } diff --git a/src/main/java/com/gitblit/transport/ssh/FileKeyPairProvider.java b/src/main/java/com/gitblit/transport/ssh/FileKeyPairProvider.java new file mode 100644 index 00000000..db0741e0 --- /dev/null +++ b/src/main/java/com/gitblit/transport/ssh/FileKeyPairProvider.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.gitblit.transport.ssh; + +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.security.KeyPair; +import java.util.Arrays; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider; +import org.apache.sshd.common.util.SecurityUtils; +import org.bouncycastle.openssl.PEMDecryptorProvider; +import org.bouncycastle.openssl.PEMEncryptedKeyPair; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.PasswordFinder; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; + +/** + * This host key provider loads private keys from the specified files. + * + * Note that this class has a direct dependency on BouncyCastle and won't work + * unless it has been correctly registered as a security provider. + * + * @author Apache MINA SSHD Project + */ +public class FileKeyPairProvider extends AbstractKeyPairProvider { + + private String[] files; + private PasswordFinder passwordFinder; + + public FileKeyPairProvider() { + } + + public FileKeyPairProvider(String[] files) { + this.files = files; + } + + public FileKeyPairProvider(String[] files, PasswordFinder passwordFinder) { + this.files = files; + this.passwordFinder = passwordFinder; + } + + public String[] getFiles() { + return files; + } + + public void setFiles(String[] files) { + this.files = files; + } + + public PasswordFinder getPasswordFinder() { + return passwordFinder; + } + + public void setPasswordFinder(PasswordFinder passwordFinder) { + this.passwordFinder = passwordFinder; + } + + public Iterable loadKeys() { + if (!SecurityUtils.isBouncyCastleRegistered()) { + throw new IllegalStateException("BouncyCastle must be registered as a JCE provider"); + } + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + private final Iterator iterator = Arrays.asList(files).iterator(); + private KeyPair nextKeyPair; + private boolean nextKeyPairSet = false; + @Override + public boolean hasNext() { + return nextKeyPairSet || setNextObject(); + } + @Override + public KeyPair next() { + if (!nextKeyPairSet) { + if (!setNextObject()) { + throw new NoSuchElementException(); + } + } + nextKeyPairSet = false; + return nextKeyPair; + } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + private boolean setNextObject() { + while (iterator.hasNext()) { + String file = iterator.next(); + nextKeyPair = doLoadKey(file); + if (nextKeyPair != null) { + nextKeyPairSet = true; + return true; + } + } + return false; + } + + }; + } + }; + } + + protected KeyPair doLoadKey(String file) { + try { + PEMParser r = new PEMParser(new InputStreamReader(new FileInputStream(file))); + try { + Object o = r.readObject(); + + JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter(); + pemConverter.setProvider("BC"); + if (passwordFinder != null && o instanceof PEMEncryptedKeyPair) { + JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder(); + PEMDecryptorProvider pemDecryptor = decryptorBuilder.build(passwordFinder.getPassword()); + o = pemConverter.getKeyPair(((PEMEncryptedKeyPair) o).decryptKeyPair(pemDecryptor)); + } + + if (o instanceof PEMKeyPair) { + o = pemConverter.getKeyPair((PEMKeyPair)o); + return (KeyPair) o; + } else if (o instanceof KeyPair) { + return (KeyPair) o; + } + } finally { + r.close(); + } + } catch (Exception e) { + log.warn("Unable to read key " + file, e); + } + return null; + } + +} diff --git a/src/main/java/com/gitblit/transport/ssh/NonForwardingFilter.java b/src/main/java/com/gitblit/transport/ssh/NonForwardingFilter.java index 4bd75d59..29f7750d 100644 --- a/src/main/java/com/gitblit/transport/ssh/NonForwardingFilter.java +++ b/src/main/java/com/gitblit/transport/ssh/NonForwardingFilter.java @@ -15,13 +15,14 @@ */ package com.gitblit.transport.ssh; -import org.apache.sshd.common.ForwardingFilter; -import org.apache.sshd.common.Session; import org.apache.sshd.common.SshdSocketAddress; +import org.apache.sshd.common.session.Session; +import org.apache.sshd.server.forward.ForwardingFilter; public class NonForwardingFilter implements ForwardingFilter { + @Override - public boolean canConnect(SshdSocketAddress address, Session session) { + public boolean canConnect(Type type, SshdSocketAddress address, Session session) { return false; } diff --git a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java index 68a2e901..b6fae25e 100644 --- a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java +++ b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java @@ -28,20 +28,19 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.sshd.SshServer; import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.io.IoServiceFactoryFactory; import org.apache.sshd.common.io.mina.MinaServiceFactoryFactory; import org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory; -import org.apache.sshd.common.keyprovider.FileKeyPairProvider; import org.apache.sshd.common.util.SecurityUtils; -import org.apache.sshd.server.UserAuth; +import org.apache.sshd.server.SshServer; import org.apache.sshd.server.auth.CachingPublicKeyAuthenticator; -import org.apache.sshd.server.auth.UserAuthKeyboardInteractive; -import org.apache.sshd.server.auth.UserAuthPassword; -import org.apache.sshd.server.auth.UserAuthPublicKey; +import org.apache.sshd.server.auth.UserAuth; +import org.apache.sshd.server.auth.UserAuthKeyboardInteractiveFactory; +import org.apache.sshd.server.auth.UserAuthPasswordFactory; +import org.apache.sshd.server.auth.UserAuthPublicKeyFactory; import org.apache.sshd.server.auth.gss.GSSAuthenticator; -import org.apache.sshd.server.auth.gss.UserAuthGSS; +import org.apache.sshd.server.auth.gss.UserAuthGSSFactory; import org.bouncycastle.openssl.PEMWriter; import org.eclipse.jgit.internal.JGitText; import org.slf4j.Logger; @@ -156,17 +155,17 @@ public class SshDaemon { switch (authenticatorName) { case "gssapi-with-mic": if(gssAuthenticator != null) { - userAuthFactories.add(new UserAuthGSS.Factory()); + userAuthFactories.add(new UserAuthGSSFactory()); } break; case "publickey": - userAuthFactories.add(new UserAuthPublicKey.Factory()); + userAuthFactories.add(new UserAuthPublicKeyFactory()); break; case "password": - userAuthFactories.add(new UserAuthPassword.Factory()); + userAuthFactories.add(new UserAuthPasswordFactory()); break; case "keyboard-interactive": - userAuthFactories.add(new UserAuthKeyboardInteractive.Factory()); + userAuthFactories.add(new UserAuthKeyboardInteractiveFactory()); break; default: log.error("Unknown ssh authenticator: '{}'", authenticatorName); @@ -257,7 +256,7 @@ public class SshDaemon { try { ((SshCommandFactory) sshd.getCommandFactory()).stop(); sshd.stop(); - } catch (InterruptedException e) { + } catch (IOException e) { log.error("SSH Daemon stop interrupted", e); } } diff --git a/src/main/java/com/gitblit/transport/ssh/SshDaemonClient.java b/src/main/java/com/gitblit/transport/ssh/SshDaemonClient.java index a5d4c3dd..af25251b 100644 --- a/src/main/java/com/gitblit/transport/ssh/SshDaemonClient.java +++ b/src/main/java/com/gitblit/transport/ssh/SshDaemonClient.java @@ -17,7 +17,7 @@ package com.gitblit.transport.ssh; import java.net.SocketAddress; -import org.apache.sshd.common.Session.AttributeKey; +import org.apache.sshd.common.session.Session.AttributeKey; import com.gitblit.models.UserModel; diff --git a/src/main/java/com/gitblit/transport/ssh/SshKey.java b/src/main/java/com/gitblit/transport/ssh/SshKey.java index 9c99d1a5..9fd1005a 100644 --- a/src/main/java/com/gitblit/transport/ssh/SshKey.java +++ b/src/main/java/com/gitblit/transport/ssh/SshKey.java @@ -22,7 +22,8 @@ import java.util.List; import org.apache.commons.codec.binary.Base64; import org.apache.sshd.common.SshException; -import org.apache.sshd.common.util.Buffer; +import org.apache.sshd.common.util.buffer.Buffer; +import org.apache.sshd.common.util.buffer.ByteArrayBuffer; import org.eclipse.jgit.lib.Constants; import com.gitblit.Constants.AccessPermission; @@ -72,7 +73,7 @@ public class SshKey implements Serializable { } final byte[] bin = Base64.decodeBase64(Constants.encodeASCII(parts[1])); try { - publicKey = new Buffer(bin).getRawPublicKey(); + publicKey = new ByteArrayBuffer(bin).getRawPublicKey(); } catch (SshException e) { throw new RuntimeException(e); } @@ -145,7 +146,7 @@ public class SshKey implements Serializable { public String getRawData() { if (rawData == null && publicKey != null) { // build the raw data manually from the public key - Buffer buf = new Buffer(); + Buffer buf = new ByteArrayBuffer(); // 1: identify the algorithm buf.putRawPublicKey(publicKey); diff --git a/src/main/java/com/gitblit/transport/ssh/SshKeyAuthenticator.java b/src/main/java/com/gitblit/transport/ssh/SshKeyAuthenticator.java index c28a2ed6..dc9d8a4e 100644 --- a/src/main/java/com/gitblit/transport/ssh/SshKeyAuthenticator.java +++ b/src/main/java/com/gitblit/transport/ssh/SshKeyAuthenticator.java @@ -19,7 +19,7 @@ import java.security.PublicKey; import java.util.List; import java.util.Locale; -import org.apache.sshd.server.PublickeyAuthenticator; +import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator; import org.apache.sshd.server.session.ServerSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/gitblit/transport/ssh/SshServerSession.java b/src/main/java/com/gitblit/transport/ssh/SshServerSession.java index d12a6be2..02504ec8 100644 --- a/src/main/java/com/gitblit/transport/ssh/SshServerSession.java +++ b/src/main/java/com/gitblit/transport/ssh/SshServerSession.java @@ -19,10 +19,10 @@ import org.apache.sshd.common.future.CloseFuture; import org.apache.sshd.common.future.SshFutureListener; import org.apache.sshd.common.io.IoSession; import org.apache.sshd.server.ServerFactoryManager; -import org.apache.sshd.server.session.ServerSession; +import org.apache.sshd.server.session.ServerSessionImpl; // Expose addition of close session listeners -class SshServerSession extends ServerSession { +class SshServerSession extends ServerSessionImpl { SshServerSession(ServerFactoryManager server, IoSession ioSession) throws Exception { super(server, ioSession); diff --git a/src/main/java/com/gitblit/transport/ssh/SshServerSessionFactory.java b/src/main/java/com/gitblit/transport/ssh/SshServerSessionFactory.java index 0c018f02..bc67cec0 100644 --- a/src/main/java/com/gitblit/transport/ssh/SshServerSessionFactory.java +++ b/src/main/java/com/gitblit/transport/ssh/SshServerSessionFactory.java @@ -67,6 +67,6 @@ public class SshServerSessionFactory extends SessionFactory { @Override protected AbstractSession doCreateSession(IoSession ioSession) throws Exception { - return new SshServerSession(server, ioSession); + return new SshServerSession(getServer(), ioSession); } } diff --git a/src/main/java/com/gitblit/transport/ssh/UsernamePasswordAuthenticator.java b/src/main/java/com/gitblit/transport/ssh/UsernamePasswordAuthenticator.java index d7c4fe5e..a6d77ec4 100644 --- a/src/main/java/com/gitblit/transport/ssh/UsernamePasswordAuthenticator.java +++ b/src/main/java/com/gitblit/transport/ssh/UsernamePasswordAuthenticator.java @@ -17,7 +17,7 @@ package com.gitblit.transport.ssh; import java.util.Locale; -import org.apache.sshd.server.PasswordAuthenticator; +import org.apache.sshd.server.auth.password.PasswordAuthenticator; import org.apache.sshd.server.session.ServerSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/test/java/com/gitblit/tests/SshDaemonTest.java b/src/test/java/com/gitblit/tests/SshDaemonTest.java index dcaeaff8..c5deb7d5 100644 --- a/src/test/java/com/gitblit/tests/SshDaemonTest.java +++ b/src/test/java/com/gitblit/tests/SshDaemonTest.java @@ -19,8 +19,8 @@ import java.io.File; import java.text.MessageFormat; import java.util.List; -import org.apache.sshd.ClientSession; -import org.apache.sshd.SshClient; +import org.apache.sshd.client.SshClient; +import org.apache.sshd.client.session.ClientSession; import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.revwalk.RevCommit; diff --git a/src/test/java/com/gitblit/tests/SshUnitTest.java b/src/test/java/com/gitblit/tests/SshUnitTest.java index 3def700d..453f3043 100644 --- a/src/test/java/com/gitblit/tests/SshUnitTest.java +++ b/src/test/java/com/gitblit/tests/SshUnitTest.java @@ -28,14 +28,14 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.sshd.ClientChannel; -import org.apache.sshd.ClientSession; -import org.apache.sshd.SshClient; import org.apache.sshd.client.ServerKeyVerifier; +import org.apache.sshd.client.SshClient; +import org.apache.sshd.client.auth.UserAuth; +import org.apache.sshd.client.auth.UserAuthPublicKeyFactory; +import org.apache.sshd.client.channel.ClientChannel; +import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.util.SecurityUtils; -import org.apache.sshd.client.UserAuth; -import org.apache.sshd.client.auth.UserAuthPublicKey; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -108,7 +108,7 @@ public abstract class SshUnitTest extends GitblitUnitTest { } }); List> userAuthFactories = new ArrayList<>(); - userAuthFactories.add(new UserAuthPublicKey.Factory()); + userAuthFactories.add(new UserAuthPublicKeyFactory()); client.setUserAuthFactories(userAuthFactories); client.start(); return client; -- 2.39.5