|
36 | 36 | import java.util.Collections;
|
37 | 37 | import java.util.List;
|
38 | 38 | import java.util.Objects;
|
| 39 | +import java.util.Optional; |
39 | 40 | import java.util.TreeSet;
|
40 | 41 | import java.util.concurrent.atomic.AtomicReference;
|
41 | 42 | import java.util.function.Supplier;
|
| 43 | +import java.util.stream.Collectors; |
42 | 44 |
|
43 | 45 | import org.apache.sshd.client.config.hosts.KnownHostEntry;
|
44 | 46 | import org.apache.sshd.client.config.hosts.KnownHostHashValue;
|
@@ -267,36 +269,63 @@ protected PublicKeyEntryResolver getFallbackPublicKeyEntryResolver() {
|
267 | 269 | protected boolean acceptKnownHostEntries(
|
268 | 270 | ClientSession clientSession, SocketAddress remoteAddress, PublicKey serverKey,
|
269 | 271 | Collection<HostEntryPair> knownHosts) {
|
270 |
| - // TODO allow for several candidates and check if ANY of them matches the key and has 'revoked' marker |
271 |
| - HostEntryPair match = findKnownHostEntry(clientSession, remoteAddress, knownHosts); |
272 |
| - if (match == null) { |
| 272 | + |
| 273 | + List<HostEntryPair> hostMatches = findKnownHostEntries(clientSession, remoteAddress, knownHosts); |
| 274 | + if (hostMatches.isEmpty()) { |
273 | 275 | return acceptUnknownHostKey(clientSession, remoteAddress, serverKey);
|
274 | 276 | }
|
275 | 277 |
|
276 |
| - KnownHostEntry entry = match.getHostEntry(); |
277 |
| - PublicKey expected = match.getServerKey(); |
278 |
| - if (KeyUtils.compareKeys(expected, serverKey)) { |
279 |
| - return acceptKnownHostEntry(clientSession, remoteAddress, serverKey, entry); |
| 278 | + String serverKeyType = KeyUtils.getKeyType(serverKey); |
| 279 | + |
| 280 | + List<HostEntryPair> keyMatches = hostMatches.stream() |
| 281 | + .filter(entry -> serverKeyType.equals(entry.getHostEntry().getKeyEntry().getKeyType())) |
| 282 | + .filter(k -> KeyUtils.compareKeys(k.getServerKey(), serverKey)) |
| 283 | + .collect(Collectors.toList()); |
| 284 | + |
| 285 | + if (keyMatches.stream() |
| 286 | + .anyMatch(k -> "revoked".equals(k.getHostEntry().getMarker()))) { |
| 287 | + log.debug("acceptKnownHostEntry({})[{}] key={}-{} marked as revoked", |
| 288 | + clientSession, remoteAddress, KeyUtils.getKeyType(serverKey), KeyUtils.getFingerPrint(serverKey)); |
| 289 | + return false; |
280 | 290 | }
|
281 | 291 |
|
| 292 | + if (!keyMatches.isEmpty()) { |
| 293 | + return true; |
| 294 | + } |
| 295 | + |
| 296 | + Optional<HostEntryPair> anyNonRevokedMatch = hostMatches.stream() |
| 297 | + .filter(k -> !"revoked".equals(k.getHostEntry().getMarker())) |
| 298 | + .findAny(); |
| 299 | + |
| 300 | + if (!anyNonRevokedMatch.isPresent()) { |
| 301 | + return acceptUnknownHostKey(clientSession, remoteAddress, serverKey); |
| 302 | + } |
| 303 | + |
| 304 | + KnownHostEntry entry = anyNonRevokedMatch.get().getHostEntry(); |
| 305 | + PublicKey expected = anyNonRevokedMatch.get().getServerKey(); |
| 306 | + |
282 | 307 | try {
|
283 |
| - if (!acceptModifiedServerKey(clientSession, remoteAddress, entry, expected, serverKey)) { |
284 |
| - return false; |
| 308 | + if (acceptModifiedServerKey(clientSession, remoteAddress, entry, expected, serverKey)) { |
| 309 | + updateModifiedServerKey(clientSession, remoteAddress, serverKey, knownHosts, anyNonRevokedMatch.get()); |
| 310 | + return true; |
285 | 311 | }
|
286 | 312 | } catch (Throwable t) {
|
287 | 313 | warn("acceptKnownHostEntries({})[{}] failed ({}) to accept modified server key: {}",
|
288 | 314 | clientSession, remoteAddress, t.getClass().getSimpleName(), t.getMessage(), t);
|
289 |
| - return false; |
290 | 315 | }
|
291 | 316 |
|
| 317 | + return false; |
| 318 | + } |
| 319 | + |
| 320 | + protected void updateModifiedServerKey( |
| 321 | + ClientSession clientSession, SocketAddress remoteAddress, PublicKey serverKey, Collection<HostEntryPair> knownHosts, |
| 322 | + HostEntryPair match) { |
292 | 323 | Path file = getPath();
|
293 | 324 | try {
|
294 | 325 | updateModifiedServerKey(clientSession, remoteAddress, match, serverKey, file, knownHosts);
|
295 | 326 | } catch (Throwable t) {
|
296 | 327 | handleModifiedServerKeyUpdateFailure(clientSession, remoteAddress, match, serverKey, file, knownHosts, t);
|
297 | 328 | }
|
298 |
| - |
299 |
| - return true; |
300 | 329 | }
|
301 | 330 |
|
302 | 331 | /**
|
@@ -460,74 +489,45 @@ protected void handleModifiedServerKeyUpdateFailure(
|
460 | 489 | clientSession, remoteAddress, reason.getClass().getSimpleName(), match, reason.getMessage(), reason);
|
461 | 490 | }
|
462 | 491 |
|
463 |
| - /** |
464 |
| - * Invoked <U>after</U> known host entry located and keys match - by default checks that entry has not been revoked |
465 |
| - * |
466 |
| - * @param clientSession The {@link ClientSession} |
467 |
| - * @param remoteAddress The remote host address |
468 |
| - * @param serverKey The presented server {@link PublicKey} |
469 |
| - * @param entry The {@link KnownHostEntry} value - if {@code null} then no known matching host entry was |
470 |
| - * found - default will call |
471 |
| - * {@link #acceptUnknownHostKey(ClientSession, SocketAddress, PublicKey)} |
472 |
| - * @return {@code true} if OK to accept the server |
473 |
| - */ |
474 |
| - protected boolean acceptKnownHostEntry( |
475 |
| - ClientSession clientSession, SocketAddress remoteAddress, PublicKey serverKey, KnownHostEntry entry) { |
476 |
| - if (entry == null) { // not really expected, but manage it |
477 |
| - return acceptUnknownHostKey(clientSession, remoteAddress, serverKey); |
478 |
| - } |
479 |
| - |
480 |
| - if ("revoked".equals(entry.getMarker())) { |
481 |
| - log.debug("acceptKnownHostEntry({})[{}] key={}-{} marked as {}", |
482 |
| - clientSession, remoteAddress, KeyUtils.getKeyType(serverKey), KeyUtils.getFingerPrint(serverKey), |
483 |
| - entry.getMarker()); |
484 |
| - return false; |
485 |
| - } |
486 |
| - |
487 |
| - if (log.isDebugEnabled()) { |
488 |
| - log.debug("acceptKnownHostEntry({})[{}] matched key={}-{}", |
489 |
| - clientSession, remoteAddress, KeyUtils.getKeyType(serverKey), KeyUtils.getFingerPrint(serverKey)); |
490 |
| - } |
491 |
| - return true; |
492 |
| - } |
493 |
| - |
494 |
| - protected HostEntryPair findKnownHostEntry( |
| 492 | + protected List<HostEntryPair> findKnownHostEntries( |
495 | 493 | ClientSession clientSession, SocketAddress remoteAddress, Collection<HostEntryPair> knownHosts) {
|
496 | 494 | if (GenericUtils.isEmpty(knownHosts)) {
|
497 |
| - return null; |
| 495 | + return Collections.emptyList(); |
498 | 496 | }
|
499 | 497 |
|
500 | 498 | Collection<SshdSocketAddress> candidates = resolveHostNetworkIdentities(clientSession, remoteAddress);
|
501 | 499 | boolean debugEnabled = log.isDebugEnabled();
|
502 | 500 | if (debugEnabled) {
|
503 |
| - log.debug("findKnownHostEntry({})[{}] host network identities: {}", |
| 501 | + log.debug("findKnownHostEntries({})[{}] host network identities: {}", |
504 | 502 | clientSession, remoteAddress, candidates);
|
505 | 503 | }
|
506 | 504 |
|
507 | 505 | if (GenericUtils.isEmpty(candidates)) {
|
508 |
| - return null; |
| 506 | + return Collections.emptyList(); |
509 | 507 | }
|
510 | 508 |
|
511 |
| - for (HostEntryPair match : knownHosts) { |
512 |
| - KnownHostEntry entry = match.getHostEntry(); |
| 509 | + List<HostEntryPair> matches = new ArrayList<>(); |
| 510 | + for (HostEntryPair line : knownHosts) { |
| 511 | + KnownHostEntry entry = line.getHostEntry(); |
513 | 512 | for (SshdSocketAddress host : candidates) {
|
514 | 513 | try {
|
515 | 514 | if (entry.isHostMatch(host.getHostName(), host.getPort())) {
|
516 | 515 | if (debugEnabled) {
|
517 |
| - log.debug("findKnownHostEntry({})[{}] matched host={} for entry={}", |
| 516 | + log.debug("findKnownHostEntries({})[{}] matched host={} for entry={}", |
518 | 517 | clientSession, remoteAddress, host, entry);
|
519 | 518 | }
|
520 |
| - return match; |
| 519 | + matches.add(line); |
| 520 | + break; |
521 | 521 | }
|
522 | 522 | } catch (RuntimeException | Error e) {
|
523 |
| - warn("findKnownHostEntry({})[{}] failed ({}) to check host={} for entry={}: {}", |
| 523 | + warn("findKnownHostEntries({})[{}] failed ({}) to check host={} for entry={}: {}", |
524 | 524 | clientSession, remoteAddress, e.getClass().getSimpleName(),
|
525 | 525 | host, entry.getConfigLine(), e.getMessage(), e);
|
526 | 526 | }
|
527 | 527 | }
|
528 | 528 | }
|
529 | 529 |
|
530 |
| - return null; // no match found |
| 530 | + return matches; |
531 | 531 | }
|
532 | 532 |
|
533 | 533 | /**
|
|
0 commit comments