You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

JGitKexExtensionHandler.java 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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;
  11. import java.io.IOException;
  12. import java.util.ArrayList;
  13. import java.util.Collection;
  14. import java.util.Iterator;
  15. import java.util.List;
  16. import java.util.Map;
  17. import java.util.Set;
  18. import java.util.TreeSet;
  19. import org.apache.sshd.common.AttributeRepository.AttributeKey;
  20. import org.apache.sshd.common.NamedFactory;
  21. import org.apache.sshd.common.kex.KexProposalOption;
  22. import org.apache.sshd.common.kex.extension.KexExtensionHandler;
  23. import org.apache.sshd.common.kex.extension.KexExtensions;
  24. import org.apache.sshd.common.kex.extension.parser.ServerSignatureAlgorithms;
  25. import org.apache.sshd.common.session.Session;
  26. import org.apache.sshd.common.signature.Signature;
  27. import org.apache.sshd.common.util.logging.AbstractLoggingBean;
  28. import org.eclipse.jgit.util.StringUtils;
  29. /**
  30. * Do not use the DefaultClientKexExtensionHandler from sshd; it doesn't work
  31. * properly because of misconceptions. See SSHD-1141.
  32. *
  33. * @see <a href="https://issues.apache.org/jira/browse/SSHD-1141">SSHD-1141</a>
  34. */
  35. public class JGitKexExtensionHandler extends AbstractLoggingBean
  36. implements KexExtensionHandler {
  37. /** Singleton instance. */
  38. public static final JGitKexExtensionHandler INSTANCE = new JGitKexExtensionHandler();
  39. /**
  40. * Session {@link AttributeKey} used to store whether the extension
  41. * indicator was already sent.
  42. */
  43. private static final AttributeKey<Boolean> CLIENT_PROPOSAL_MADE = new AttributeKey<>();
  44. /**
  45. * Session {@link AttributeKey} storing the algorithms announced by the
  46. * server as known.
  47. */
  48. public static final AttributeKey<Set<String>> SERVER_ALGORITHMS = new AttributeKey<>();
  49. private JGitKexExtensionHandler() {
  50. // No public instantiation for singleton
  51. }
  52. @Override
  53. public boolean isKexExtensionsAvailable(Session session,
  54. AvailabilityPhase phase) throws IOException {
  55. return !AvailabilityPhase.PREKEX.equals(phase);
  56. }
  57. @Override
  58. public void handleKexInitProposal(Session session, boolean initiator,
  59. Map<KexProposalOption, String> proposal) throws IOException {
  60. // If it's the very first time, we may add the marker telling the server
  61. // that we are ready to handle SSH_MSG_EXT_INFO
  62. if (session == null || session.isServerSession() || !initiator) {
  63. return;
  64. }
  65. if (session.getAttribute(CLIENT_PROPOSAL_MADE) != null) {
  66. return;
  67. }
  68. String kexAlgorithms = proposal.get(KexProposalOption.SERVERKEYS);
  69. if (StringUtils.isEmptyOrNull(kexAlgorithms)) {
  70. return;
  71. }
  72. List<String> algorithms = new ArrayList<>();
  73. // We're a client. We mustn't send the server extension, and we should
  74. // send the client extension only once.
  75. for (String algo : kexAlgorithms.split(",")) { //$NON-NLS-1$
  76. if (KexExtensions.CLIENT_KEX_EXTENSION.equalsIgnoreCase(algo)
  77. || KexExtensions.SERVER_KEX_EXTENSION
  78. .equalsIgnoreCase(algo)) {
  79. continue;
  80. }
  81. algorithms.add(algo);
  82. }
  83. // Tell the server that we want to receive SSH2_MSG_EXT_INFO
  84. algorithms.add(KexExtensions.CLIENT_KEX_EXTENSION);
  85. if (log.isDebugEnabled()) {
  86. log.debug(
  87. "handleKexInitProposal({}): proposing HostKeyAlgorithms {}", //$NON-NLS-1$
  88. session, algorithms);
  89. }
  90. proposal.put(KexProposalOption.SERVERKEYS,
  91. String.join(",", algorithms)); //$NON-NLS-1$
  92. session.setAttribute(CLIENT_PROPOSAL_MADE, Boolean.TRUE);
  93. }
  94. @Override
  95. public boolean handleKexExtensionRequest(Session session, int index,
  96. int count, String name, byte[] data) throws IOException {
  97. if (ServerSignatureAlgorithms.NAME.equals(name)) {
  98. handleServerSignatureAlgorithms(session,
  99. ServerSignatureAlgorithms.INSTANCE.parseExtension(data));
  100. }
  101. return true;
  102. }
  103. /**
  104. * Perform updates after a server-sig-algs extension has been received.
  105. *
  106. * @param session
  107. * the message was received for
  108. * @param serverAlgorithms
  109. * signature algorithm names announced by the server
  110. */
  111. protected void handleServerSignatureAlgorithms(Session session,
  112. Collection<String> serverAlgorithms) {
  113. if (log.isDebugEnabled()) {
  114. log.debug("handleServerSignatureAlgorithms({}): {}", session, //$NON-NLS-1$
  115. serverAlgorithms);
  116. }
  117. // Client determines order; server says what it supports. Re-order
  118. // such that supported ones are at the front, in client order,
  119. // followed by unsupported ones, also in client order.
  120. if (serverAlgorithms != null && !serverAlgorithms.isEmpty()) {
  121. List<NamedFactory<Signature>> clientAlgorithms = new ArrayList<>(
  122. session.getSignatureFactories());
  123. if (log.isDebugEnabled()) {
  124. log.debug(
  125. "handleServerSignatureAlgorithms({}): PubkeyAcceptedAlgorithms before: {}", //$NON-NLS-1$
  126. session, clientAlgorithms);
  127. }
  128. List<NamedFactory<Signature>> unknown = new ArrayList<>();
  129. Set<String> known = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
  130. known.addAll(serverAlgorithms);
  131. for (Iterator<NamedFactory<Signature>> iter = clientAlgorithms
  132. .iterator(); iter.hasNext();) {
  133. NamedFactory<Signature> algo = iter.next();
  134. if (!known.contains(algo.getName())) {
  135. unknown.add(algo);
  136. iter.remove();
  137. }
  138. }
  139. // Re-add the unknown ones at the end. Per RFC 8308, some
  140. // servers may not announce _all_ their supported algorithms,
  141. // and a client may use unknown algorithms.
  142. clientAlgorithms.addAll(unknown);
  143. if (log.isDebugEnabled()) {
  144. log.debug(
  145. "handleServerSignatureAlgorithms({}): PubkeyAcceptedAlgorithms after: {}", //$NON-NLS-1$
  146. session, clientAlgorithms);
  147. }
  148. session.setAttribute(SERVER_ALGORITHMS, known);
  149. session.setSignatureFactories(clientAlgorithms);
  150. }
  151. }
  152. }