View Javadoc
1   /*
2    * Copyright (C) 2020 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.transport.sshd;
11  
12  import static org.junit.Assert.assertNotNull;
13  
14  import java.io.File;
15  import java.io.IOException;
16  import java.io.UncheckedIOException;
17  import java.net.InetSocketAddress;
18  import java.nio.file.Files;
19  import java.security.GeneralSecurityException;
20  import java.security.KeyPair;
21  import java.security.KeyPairGenerator;
22  import java.security.PublicKey;
23  import java.util.Arrays;
24  import java.util.Collections;
25  import java.util.Iterator;
26  import java.util.List;
27  
28  import org.apache.sshd.common.config.keys.KeyUtils;
29  import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
30  import org.apache.sshd.common.session.SessionContext;
31  import org.apache.sshd.common.util.net.SshdSocketAddress;
32  import org.eclipse.jgit.lib.Constants;
33  import org.eclipse.jgit.transport.CredentialsProvider;
34  import org.eclipse.jgit.transport.SshSessionFactory;
35  import org.eclipse.jgit.junit.ssh.SshTestHarness;
36  import org.eclipse.jgit.util.FS;
37  import org.junit.After;
38  import org.junit.Test;
39  
40  /**
41   * Test for using the SshdSessionFactory without files in ~/.ssh but with an
42   * in-memory setup, creating the factory via the builder API.
43   */
44  public class NoFilesSshBuilderTest extends SshTestHarness {
45  
46  	private PublicKey testServerKey;
47  
48  	private KeyPair testUserKey;
49  
50  	@Override
51  	protected SshSessionFactory createSessionFactory() {
52  		return new SshdSessionFactoryBuilder() //
53  				// No proxies in tests
54  				.setProxyDataFactory(null)
55  				// No ssh-agent in tests
56  				.setConnectorFactory(null)
57  				.setConfigStoreFactory((h, f, u) -> null)
58  				.setDefaultKeysProvider(f -> new KeyAuthenticator())
59  				.setServerKeyDatabase((h, s) -> new ServerKeyDatabase() {
60  
61  					@Override
62  					public List<PublicKey> lookup(String connectAddress,
63  							InetSocketAddress remoteAddress,
64  							Configuration config) {
65  						return Collections.singletonList(testServerKey);
66  					}
67  
68  					@Override
69  					public boolean accept(String connectAddress,
70  							InetSocketAddress remoteAddress,
71  							PublicKey serverKey, Configuration config,
72  							CredentialsProvider provider) {
73  						return KeyUtils.compareKeys(serverKey, testServerKey);
74  					}
75  
76  				}) //
77  				.setPreferredAuthentications("publickey")
78  				.setHomeDirectory(FS.DETECTED.userHome())
79  				.setSshDirectory(sshDir) //
80  				.build(new JGitKeyCache());
81  	}
82  
83  	private class KeyAuthenticator
84  			implements KeyIdentityProvider, Iterable<KeyPair> {
85  
86  		@Override
87  		public Iterator<KeyPair> iterator() {
88  			// Should not be called. The use of the Iterable interface in
89  			// SshdSessionFactory.getDefaultKeys() made sense in sshd 2.0.0,
90  			// but sshd 2.2.0 added the SessionContext, which although good
91  			// (without it we couldn't check here) breaks the Iterable analogy.
92  			// But we're stuck now with that interface for getDefaultKeys, and
93  			// so this override throwing an exception is unfortunately needed.
94  			throw new UnsupportedOperationException();
95  		}
96  
97  		@Override
98  		public Iterable<KeyPair> loadKeys(SessionContext session)
99  				throws IOException, GeneralSecurityException {
100 			if (!TEST_USER.equals(session.getUsername())) {
101 				return Collections.emptyList();
102 			}
103 			SshdSocketAddress remoteAddress = SshdSocketAddress
104 					.toSshdSocketAddress(session.getRemoteAddress());
105 			switch (remoteAddress.getHostName()) {
106 			case "localhost":
107 			case "127.0.0.1":
108 				return Collections.singletonList(testUserKey);
109 			default:
110 				return Collections.emptyList();
111 			}
112 		}
113 	}
114 
115 	@After
116 	public void cleanUp() {
117 		testServerKey = null;
118 		testUserKey = null;
119 	}
120 
121 	@Override
122 	protected void installConfig(String... config) {
123 		File configFile = new File(sshDir, Constants.CONFIG);
124 		if (config != null) {
125 			try {
126 				Files.write(configFile.toPath(), Arrays.asList(config));
127 			} catch (IOException e) {
128 				throw new UncheckedIOException(e);
129 			}
130 		}
131 	}
132 
133 	@Test
134 	public void testCloneWithBuiltInKeys() throws Exception {
135 		// This test should fail unless our in-memory setup is taken: no
136 		// known_hosts file, a config that specifies a non-existing key,
137 		// and the test is using a newly generated KeyPairs anyway.
138 		KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
139 		generator.initialize(2048);
140 		testUserKey = generator.generateKeyPair();
141 		KeyPair hostKey = generator.generateKeyPair();
142 		server.addHostKey(hostKey, true);
143 		testServerKey = hostKey.getPublic();
144 		assertNotNull(testServerKey);
145 		assertNotNull(testUserKey);
146 		server.setTestUserPublicKey(testUserKey.getPublic());
147 		cloneWith(
148 				"ssh://" + TEST_USER + "@localhost:" + testPort
149 						+ "/doesntmatter",
150 				new File(getTemporaryDirectory(), "cloned"), null, //
151 				"Host localhost", //
152 				"IdentityFile "
153 						+ new File(sshDir, "does_not_exist").getAbsolutePath());
154 	}
155 
156 }