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  				.setConfigStoreFactory((h, f, u) -> null)
54  				.setDefaultKeysProvider(f -> new KeyAuthenticator())
55  				.setServerKeyDatabase((h, s) -> new ServerKeyDatabase() {
56  
57  					@Override
58  					public List<PublicKey> lookup(String connectAddress,
59  							InetSocketAddress remoteAddress,
60  							Configuration config) {
61  						return Collections.singletonList(testServerKey);
62  					}
63  
64  					@Override
65  					public boolean accept(String connectAddress,
66  							InetSocketAddress remoteAddress,
67  							PublicKey serverKey, Configuration config,
68  							CredentialsProvider provider) {
69  						return KeyUtils.compareKeys(serverKey, testServerKey);
70  					}
71  
72  				}) //
73  				.setPreferredAuthentications("publickey")
74  				.setHomeDirectory(FS.DETECTED.userHome())
75  				.setSshDirectory(sshDir) //
76  				.build(new JGitKeyCache());
77  	}
78  
79  	private class KeyAuthenticator
80  			implements KeyIdentityProvider, Iterable<KeyPair> {
81  
82  		@Override
83  		public Iterator<KeyPair> iterator() {
84  			// Should not be called. The use of the Iterable interface in
85  			// SshdSessionFactory.getDefaultKeys() made sense in sshd 2.0.0,
86  			// but sshd 2.2.0 added the SessionContext, which although good
87  			// (without it we couldn't check here) breaks the Iterable analogy.
88  			// But we're stuck now with that interface for getDefaultKeys, and
89  			// so this override throwing an exception is unfortunately needed.
90  			throw new UnsupportedOperationException();
91  		}
92  
93  		@Override
94  		public Iterable<KeyPair> loadKeys(SessionContext session)
95  				throws IOException, GeneralSecurityException {
96  			if (!TEST_USER.equals(session.getUsername())) {
97  				return Collections.emptyList();
98  			}
99  			SshdSocketAddress remoteAddress = SshdSocketAddress
100 					.toSshdSocketAddress(session.getRemoteAddress());
101 			switch (remoteAddress.getHostName()) {
102 			case "localhost":
103 			case "127.0.0.1":
104 				return Collections.singletonList(testUserKey);
105 			default:
106 				return Collections.emptyList();
107 			}
108 		}
109 	}
110 
111 	@After
112 	public void cleanUp() {
113 		testServerKey = null;
114 		testUserKey = null;
115 	}
116 
117 	@Override
118 	protected void installConfig(String... config) {
119 		File configFile = new File(sshDir, Constants.CONFIG);
120 		if (config != null) {
121 			try {
122 				Files.write(configFile.toPath(), Arrays.asList(config));
123 			} catch (IOException e) {
124 				throw new UncheckedIOException(e);
125 			}
126 		}
127 	}
128 
129 	@Test
130 	public void testCloneWithBuiltInKeys() throws Exception {
131 		// This test should fail unless our in-memory setup is taken: no
132 		// known_hosts file, a config that specifies a non-existing key,
133 		// and the test is using a newly generated KeyPairs anyway.
134 		KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
135 		generator.initialize(2048);
136 		testUserKey = generator.generateKeyPair();
137 		KeyPair hostKey = generator.generateKeyPair();
138 		server.addHostKey(hostKey, true);
139 		testServerKey = hostKey.getPublic();
140 		assertNotNull(testServerKey);
141 		assertNotNull(testUserKey);
142 		server.setTestUserPublicKey(testUserKey.getPublic());
143 		cloneWith(
144 				"ssh://" + TEST_USER + "@localhost:" + testPort
145 						+ "/doesntmatter",
146 				new File(getTemporaryDirectory(), "cloned"), null, //
147 				"Host localhost", //
148 				"IdentityFile "
149 						+ new File(sshDir, "does_not_exist").getAbsolutePath());
150 	}
151 
152 }