Skip to content

Commit 68017a0

Browse files
Thomas Wolfmsohn
Thomas Wolf
authored andcommitted
sshd: prepare for using an SSH agent
Add interfaces Connector and ConnectorFactory. A "connector" is just something that knows how to connect to an ssh-agent and then can make simple synchronous RPC-style requests (request-reply). Add a way to customize an SshdSessionFactory with a ConnectorFactory. Provide a default setup using the Java ServiceLoader mechanism to discover an ConnectorFactory. Implement an SshAgentClient in the internal part. Unfortunately we cannot re-use the implementation in Apache MINA sshd: it's hard-wired to Apache Tomcat APR, and it's also buggy. No behavior changes yet since there is nothing that would provide an actual ConnectorFactory. So for Apache MINA sshd, the SshAgentFactory remains null as before. Change-Id: I963a3d181357df2bdb66298bc702f2b9a6607a30 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
1 parent c04884f commit 68017a0

File tree

12 files changed

+719
-4
lines changed

12 files changed

+719
-4
lines changed

org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.0.0";x-inter
2323
org.apache.sshd.common.signature,
2424
org.apache.sshd.common.util.buffer,
2525
org.eclipse.jgit.transport",
26+
org.eclipse.jgit.internal.transport.sshd.agent;version="6.0.0";x-internal:=true,
2627
org.eclipse.jgit.internal.transport.sshd.auth;version="6.0.0";x-internal:=true,
2728
org.eclipse.jgit.internal.transport.sshd.proxy;version="6.0.0";x-friends:="org.eclipse.jgit.ssh.apache.test",
2829
org.eclipse.jgit.transport.sshd;version="6.0.0";
@@ -31,7 +32,8 @@ Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.0.0";x-inter
3132
org.apache.sshd.common.keyprovider,
3233
org.eclipse.jgit.util,
3334
org.apache.sshd.client.session,
34-
org.apache.sshd.client.keyverifier"
35+
org.apache.sshd.client.keyverifier",
36+
org.eclipse.jgit.transport.sshd.agent;version="6.0.0"
3537
Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
3638
org.apache.sshd.agent;version="[2.7.0,2.8.0)",
3739
org.apache.sshd.client;version="[2.7.0,2.8.0)",

org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties

+5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ identityFileNoKey=No keys found in identity {0}
1919
identityFileMultipleKeys=Multiple key pairs found in identity {0}
2020
identityFileNotFound=Skipping identity ''{0}'': file not found
2121
identityFileUnsupportedFormat=Unsupported format in identity {0}
22+
invalidSignatureAlgorithm=Signature algorithm ''{0}'' is not valid for a key of type ''{1}''
2223
kexServerKeyInvalid=Server key did not validate
2324
keyEncryptedMsg=Key ''{0}'' is encrypted. Enter the passphrase to decrypt it.
2425
keyEncryptedPrompt=Passphrase
@@ -84,6 +85,10 @@ serverIdTooLong=Server identification is longer than 255 characters (including l
8485
serverIdWithNul=Server identification contains a NUL character: {0}
8586
sessionCloseFailed=Closing the session failed
8687
sessionWithoutUsername=SSH session created without user name; cannot authenticate
88+
sshAgentReplyLengthError=Invalid SSH agent reply message length {0} after command {1}
89+
sshAgentReplyUnexpected=Unexpected reply from ssh-agent: {0}
90+
sshAgentShortReadBuffer=Short read from SSH agent
91+
sshAgentWrongNumberOfKeys=Invalid number of SSH agent keys: {0}
8792
sshClosingDown=Apache MINA sshd session factory is closing down; cannot create new ssh sessions on this factory
8893
sshCommandTimeout={0} timed out after {1} seconds while opening the channel
8994
sshProcessStillRunning={0} is not yet completed, cannot get exit code

org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java

+20
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@
3232
import java.util.Map;
3333
import java.util.NoSuchElementException;
3434
import java.util.Objects;
35+
import java.util.function.Supplier;
3536
import java.util.stream.Collectors;
3637

38+
import org.apache.sshd.agent.SshAgentFactory;
3739
import org.apache.sshd.client.SshClient;
3840
import org.apache.sshd.client.config.hosts.HostConfigEntry;
3941
import org.apache.sshd.client.future.ConnectFuture;
@@ -100,6 +102,8 @@ public class JGitSshClient extends SshClient {
100102

101103
private ProxyDataFactory proxyDatabase;
102104

105+
private Supplier<SshAgentFactory> agentFactorySupplier = () -> null;
106+
103107
@Override
104108
protected SessionFactory createSessionFactory() {
105109
// Override the parent's default
@@ -368,6 +372,22 @@ public CredentialsProvider getCredentialsProvider() {
368372
return credentialsProvider;
369373
}
370374

375+
@Override
376+
public SshAgentFactory getAgentFactory() {
377+
return agentFactorySupplier.get();
378+
}
379+
380+
@Override
381+
protected void checkConfig() {
382+
// The super class requires channel factories for agent forwarding if a
383+
// factory for an SSH agent is set. We haven't implemented this yet, and
384+
// we don't do SSH agent forwarding for now. Unfortunately, there is no
385+
// way to bypass this check in the super class except making
386+
// getAgentFactory() return null until after the check.
387+
super.checkConfig();
388+
agentFactorySupplier = super::getAgentFactory;
389+
}
390+
371391
/**
372392
* A {@link SessionFactory} to create our own specialized
373393
* {@link JGitClientSession}s.

org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java

+14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
/*
2+
* Copyright (C) 2018, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Distribution License v. 1.0 which is available at
6+
* https://www.eclipse.org/org/documents/edl-v10.php.
7+
*
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
110
package org.eclipse.jgit.internal.transport.sshd;
211

312
import org.eclipse.jgit.nls.NLS;
@@ -39,6 +48,7 @@ public static SshdText get() {
3948
/***/ public String identityFileMultipleKeys;
4049
/***/ public String identityFileNotFound;
4150
/***/ public String identityFileUnsupportedFormat;
51+
/***/ public String invalidSignatureAlgorithm;
4252
/***/ public String kexServerKeyInvalid;
4353
/***/ public String keyEncryptedMsg;
4454
/***/ public String keyEncryptedPrompt;
@@ -96,6 +106,10 @@ public static SshdText get() {
96106
/***/ public String serverIdWithNul;
97107
/***/ public String sessionCloseFailed;
98108
/***/ public String sessionWithoutUsername;
109+
/***/ public String sshAgentReplyLengthError;
110+
/***/ public String sshAgentReplyUnexpected;
111+
/***/ public String sshAgentShortReadBuffer;
112+
/***/ public String sshAgentWrongNumberOfKeys;
99113
/***/ public String sshClosingDown;
100114
/***/ public String sshCommandTimeout;
101115
/***/ public String sshProcessStillRunning;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Distribution License v. 1.0 which is available at
6+
* https://www.eclipse.org/org/documents/edl-v10.php.
7+
*
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
10+
package org.eclipse.jgit.internal.transport.sshd.agent;
11+
12+
import java.util.Iterator;
13+
import java.util.ServiceLoader;
14+
15+
import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
16+
17+
/**
18+
* Provides a {@link ConnectorFactory} obtained via the {@link ServiceLoader}.
19+
*/
20+
public final class ConnectorFactoryProvider {
21+
22+
private static final ConnectorFactory FACTORY = loadDefaultFactory();
23+
24+
private static ConnectorFactory loadDefaultFactory() {
25+
ServiceLoader<ConnectorFactory> loader = ServiceLoader
26+
.load(ConnectorFactory.class);
27+
Iterator<ConnectorFactory> iter = loader.iterator();
28+
while (iter.hasNext()) {
29+
ConnectorFactory candidate = iter.next();
30+
if (candidate.isSupported()) {
31+
return candidate;
32+
}
33+
}
34+
return null;
35+
36+
}
37+
38+
private ConnectorFactoryProvider() {
39+
// No instantiation
40+
}
41+
42+
/**
43+
* Retrieves the default {@link ConnectorFactory} obtained via the
44+
* {@link ServiceLoader}.
45+
*
46+
* @return the {@link ConnectorFactory}, or {@code null} if none.
47+
*/
48+
public static ConnectorFactory getDefaultFactory() {
49+
return FACTORY;
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Distribution License v. 1.0 which is available at
6+
* https://www.eclipse.org/org/documents/edl-v10.php.
7+
*
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
10+
package org.eclipse.jgit.internal.transport.sshd.agent;
11+
12+
import java.io.File;
13+
import java.io.IOException;
14+
import java.util.Collections;
15+
import java.util.List;
16+
17+
import org.apache.sshd.agent.SshAgent;
18+
import org.apache.sshd.agent.SshAgentFactory;
19+
import org.apache.sshd.agent.SshAgentServer;
20+
import org.apache.sshd.common.FactoryManager;
21+
import org.apache.sshd.common.channel.ChannelFactory;
22+
import org.apache.sshd.common.session.ConnectionService;
23+
import org.eclipse.jgit.annotations.NonNull;
24+
import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
25+
26+
/**
27+
* A factory for creating {@link SshAgentClient}s.
28+
*/
29+
public class JGitSshAgentFactory implements SshAgentFactory {
30+
31+
private final @NonNull ConnectorFactory factory;
32+
33+
private final File homeDir;
34+
35+
/**
36+
* Creates a new {@link JGitSshAgentFactory}.
37+
*
38+
* @param factory
39+
* {@link JGitSshAgentFactory} to wrap
40+
* @param homeDir
41+
* for obtaining the current local user's home directory
42+
*/
43+
public JGitSshAgentFactory(@NonNull ConnectorFactory factory,
44+
File homeDir) {
45+
this.factory = factory;
46+
this.homeDir = homeDir;
47+
}
48+
49+
@Override
50+
public List<ChannelFactory> getChannelForwardingFactories(
51+
FactoryManager manager) {
52+
// No agent forwarding supported yet.
53+
return Collections.emptyList();
54+
}
55+
56+
@Override
57+
public SshAgent createClient(FactoryManager manager) throws IOException {
58+
// sshd 2.8.0 will pass us the session here. At that point, we can get
59+
// the HostConfigEntry and extract and handle the IdentityAgent setting.
60+
// For now, pass null to let the ConnectorFactory do its default
61+
// behavior (Pageant on Windows, SSH_AUTH_SOCK on Unixes with the
62+
// jgit-builtin factory).
63+
return new SshAgentClient(factory.create(null, homeDir));
64+
}
65+
66+
@Override
67+
public SshAgentServer createServer(ConnectionService service)
68+
throws IOException {
69+
// This should be called in a server only.
70+
return null;
71+
}
72+
}

0 commit comments

Comments
 (0)