1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.eclipse.jgit.internal.transport.sshd;
44
45 import static java.text.MessageFormat.format;
46
47 import java.io.IOException;
48 import java.net.SocketAddress;
49 import java.security.PublicKey;
50 import java.util.ArrayList;
51 import java.util.Iterator;
52 import java.util.LinkedHashSet;
53 import java.util.List;
54 import java.util.Set;
55
56 import org.apache.sshd.client.ClientFactoryManager;
57 import org.apache.sshd.client.config.hosts.HostConfigEntry;
58 import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier.HostEntryPair;
59 import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
60 import org.apache.sshd.client.session.ClientSessionImpl;
61 import org.apache.sshd.common.FactoryManager;
62 import org.apache.sshd.common.SshException;
63 import org.apache.sshd.common.config.keys.KeyUtils;
64 import org.apache.sshd.common.io.IoSession;
65 import org.apache.sshd.common.io.IoWriteFuture;
66 import org.apache.sshd.common.util.Readable;
67 import org.eclipse.jgit.errors.InvalidPatternException;
68 import org.eclipse.jgit.fnmatch.FileNameMatcher;
69 import org.eclipse.jgit.internal.transport.sshd.proxy.StatefulProxyConnector;
70 import org.eclipse.jgit.transport.CredentialsProvider;
71 import org.eclipse.jgit.transport.SshConstants;
72
73
74
75
76
77
78
79
80
81
82 public class JGitClientSession extends ClientSessionImpl {
83
84 private HostConfigEntry hostConfig;
85
86 private CredentialsProvider credentialsProvider;
87
88 private StatefulProxyConnector proxyHandler;
89
90
91
92
93
94
95 public JGitClientSession(ClientFactoryManager manager, IoSession session)
96 throws Exception {
97 super(manager, session);
98 }
99
100
101
102
103
104
105 public HostConfigEntry getHostConfigEntry() {
106 return hostConfig;
107 }
108
109
110
111
112
113
114
115 public void setHostConfigEntry(HostConfigEntry hostConfig) {
116 this.hostConfig = hostConfig;
117 }
118
119
120
121
122
123
124
125 public void setCredentialsProvider(CredentialsProvider provider) {
126 credentialsProvider = provider;
127 }
128
129
130
131
132
133
134 public CredentialsProvider getCredentialsProvider() {
135 return credentialsProvider;
136 }
137
138
139
140
141
142
143
144
145 public void setProxyHandler(StatefulProxyConnector handler) {
146 proxyHandler = handler;
147 }
148
149 @Override
150 protected IoWriteFuture sendIdentification(String ident)
151 throws IOException {
152 StatefulProxyConnector proxy = proxyHandler;
153 if (proxy != null) {
154 try {
155
156
157
158 proxy.runWhenDone(() -> {
159 JGitClientSession.super.sendIdentification(ident);
160 return null;
161 });
162
163
164 return null;
165 } catch (IOException e) {
166 throw e;
167 } catch (Exception other) {
168 throw new IOException(other.getLocalizedMessage(), other);
169 }
170 } else {
171 return super.sendIdentification(ident);
172 }
173 }
174
175 @Override
176 protected byte[] sendKexInit() throws IOException {
177 StatefulProxyConnector proxy = proxyHandler;
178 if (proxy != null) {
179 try {
180
181
182
183 proxy.runWhenDone(() -> {
184 JGitClientSession.super.sendKexInit();
185 return null;
186 });
187
188
189 return null;
190 } catch (IOException e) {
191 throw e;
192 } catch (Exception other) {
193 throw new IOException(other.getLocalizedMessage(), other);
194 }
195 } else {
196 return super.sendKexInit();
197 }
198 }
199
200
201
202
203
204
205
206 @Override
207 public void messageReceived(Readable buffer) throws Exception {
208 StatefulProxyConnector proxy = proxyHandler;
209 if (proxy != null) {
210 proxy.messageReceived(getIoSession(), buffer);
211 } else {
212 super.messageReceived(buffer);
213 }
214 }
215
216 @Override
217 protected void checkKeys() throws SshException {
218 ServerKeyVerifier serverKeyVerifier = getServerKeyVerifier();
219
220
221
222 SocketAddress remoteAddress = getConnectAddress();
223 PublicKey serverKey = getKex().getServerKey();
224 if (!serverKeyVerifier.verifyServerKey(this, remoteAddress,
225 serverKey)) {
226 throw new SshException(
227 org.apache.sshd.common.SshConstants.SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE,
228 SshdText.get().kexServerKeyInvalid);
229 }
230 }
231
232 @Override
233 protected String resolveAvailableSignaturesProposal(
234 FactoryManager manager) {
235 Set<String> defaultSignatures = new LinkedHashSet<>();
236 defaultSignatures.addAll(getSignatureFactoriesNames());
237 HostConfigEntry config = resolveAttribute(
238 JGitSshClient.HOST_CONFIG_ENTRY);
239 String hostKeyAlgorithms = config
240 .getProperty(SshConstants.HOST_KEY_ALGORITHMS);
241 if (hostKeyAlgorithms != null && !hostKeyAlgorithms.isEmpty()) {
242 char first = hostKeyAlgorithms.charAt(0);
243 if (first == '+') {
244
245
246
247 return String.join(",", defaultSignatures);
248 } else if (first == '-') {
249
250 removeFromList(defaultSignatures,
251 SshConstants.HOST_KEY_ALGORITHMS,
252 hostKeyAlgorithms.substring(1));
253 if (defaultSignatures.isEmpty()) {
254
255
256 log.warn(format(
257 SshdText.get().configNoRemainingHostKeyAlgorithms,
258 hostKeyAlgorithms));
259 }
260 return String.join(",", defaultSignatures);
261 } else {
262
263
264 List<String> newNames = filteredList(defaultSignatures,
265 hostKeyAlgorithms);
266 if (newNames.isEmpty()) {
267 log.warn(format(
268 SshdText.get().configNoKnownHostKeyAlgorithms,
269 hostKeyAlgorithms));
270
271 } else {
272 return String.join(",", newNames);
273 }
274 }
275 }
276
277
278 ServerKeyVerifier verifier = getServerKeyVerifier();
279 if (verifier instanceof ServerKeyLookup) {
280 SocketAddress remoteAddress = resolvePeerAddress(
281 resolveAttribute(JGitSshClient.ORIGINAL_REMOTE_ADDRESS));
282 List<HostEntryPair> allKnownKeys = ((ServerKeyLookup) verifier)
283 .lookup(this, remoteAddress);
284 Set<String> reordered = new LinkedHashSet<>();
285 for (HostEntryPair h : allKnownKeys) {
286 PublicKey key = h.getServerKey();
287 if (key != null) {
288 String keyType = KeyUtils.getKeyType(key);
289 if (keyType != null) {
290 reordered.add(keyType);
291 }
292 }
293 }
294 reordered.addAll(defaultSignatures);
295 return String.join(",", reordered);
296 }
297 return String.join(",", defaultSignatures);
298 }
299
300 private void removeFromList(Set<String> current, String key,
301 String patterns) {
302 for (String toRemove : patterns.split("\\s*,\\s*")) {
303 if (toRemove.indexOf('*') < 0 && toRemove.indexOf('?') < 0) {
304 current.remove(toRemove);
305 continue;
306 }
307 try {
308 FileNameMatcher matcher = new FileNameMatcher(toRemove, null);
309 for (Iterator<String> i = current.iterator(); i.hasNext();) {
310 matcher.reset();
311 matcher.append(i.next());
312 if (matcher.isMatch()) {
313 i.remove();
314 }
315 }
316 } catch (InvalidPatternException e) {
317 log.warn(format(SshdText.get().configInvalidPattern, key,
318 toRemove));
319 }
320 }
321 }
322
323 private List<String> filteredList(Set<String> known, String values) {
324 List<String> newNames = new ArrayList<>();
325 for (String newValue : values.split("\\s*,\\s*")) {
326 if (known.contains(newValue)) {
327 newNames.add(newValue);
328 }
329 }
330 return newNames;
331 }
332
333 }