From 66c9c7bf876bd81ebf57a769f14f5403861f83db Mon Sep 17 00:00:00 2001 From: Thomas Wolf Date: Tue, 11 May 2021 09:04:52 +0200 Subject: Update target platform to I20210626190330 Update - com.google.gson to 2.8.7.v20210624-1215 - javaewah to 1.1.12.v20210622-2206 - org.apache.sshd.osgi to 2.7.0.v20210623-0618 - org.apache.sshd.sftp to 2.7.0.v20210623-0618 - org.tukaani.xz to 1.9.0.v20210624-1259 - Apache MINA sshd to 2.7.0 - Remove work-arounds for problems resolved upstream since 2.6.0, and adapt to upstream API changes. - update DEPENDENCIES. CQ: 23469 CQ: 23470 CQ: 23496 CQ: 23497 CQ: 23498 Bug: 574220 Change-Id: I898b216c3492f8488fbf25fa4b49f1250f86f3c8 Also-by: David Ostrovsky Signed-off-by: Thomas Wolf Signed-off-by: Matthias Sohn --- org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF | 94 ++++----- .../internal/transport/sshd/JGitClientSession.java | 12 +- .../transport/sshd/JGitKexExtensionHandler.java | 163 --------------- .../transport/sshd/JGitPublicKeyAuthFactory.java | 35 ---- .../sshd/JGitPublicKeyAuthentication.java | 224 --------------------- .../jgit/transport/sshd/SshdSessionFactory.java | 6 +- 6 files changed, 55 insertions(+), 479 deletions(-) delete mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitKexExtensionHandler.java delete mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java delete mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java (limited to 'org.eclipse.jgit.ssh.apache') diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF index 7a1ce6ba69..81f7e2146a 100644 --- a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF @@ -33,53 +33,53 @@ Export-Package: org.eclipse.jgit.internal.transport.sshd;version="5.13.0";x-inte org.apache.sshd.client.session, org.apache.sshd.client.keyverifier" Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)", - org.apache.sshd.agent;version="[2.6.0,2.7.0)", - org.apache.sshd.client;version="[2.6.0,2.7.0)", - org.apache.sshd.client.auth;version="[2.6.0,2.7.0)", - org.apache.sshd.client.auth.keyboard;version="[2.6.0,2.7.0)", - org.apache.sshd.client.auth.password;version="[2.6.0,2.7.0)", - org.apache.sshd.client.auth.pubkey;version="[2.6.0,2.7.0)", - org.apache.sshd.client.channel;version="[2.6.0,2.7.0)", - org.apache.sshd.client.config.hosts;version="[2.6.0,2.7.0)", - org.apache.sshd.client.config.keys;version="[2.6.0,2.7.0)", - org.apache.sshd.client.future;version="[2.6.0,2.7.0)", - org.apache.sshd.client.keyverifier;version="[2.6.0,2.7.0)", - org.apache.sshd.client.session;version="[2.6.0,2.7.0)", - org.apache.sshd.client.session.forward;version="[2.6.0,2.7.0)", - org.apache.sshd.common;version="[2.6.0,2.7.0)", - org.apache.sshd.common.auth;version="[2.6.0,2.7.0)", - org.apache.sshd.common.channel;version="[2.6.0,2.7.0)", - org.apache.sshd.common.compression;version="[2.6.0,2.7.0)", - org.apache.sshd.common.config.keys;version="[2.6.0,2.7.0)", - org.apache.sshd.common.config.keys.loader;version="[2.6.0,2.7.0)", - org.apache.sshd.common.config.keys.loader.openssh.kdf;version="[2.6.0,2.7.0)", - org.apache.sshd.common.digest;version="[2.6.0,2.7.0)", - org.apache.sshd.common.forward;version="[2.6.0,2.7.0)", - org.apache.sshd.common.future;version="[2.6.0,2.7.0)", - org.apache.sshd.common.helpers;version="[2.6.0,2.7.0)", - org.apache.sshd.common.io;version="[2.6.0,2.7.0)", - org.apache.sshd.common.kex;version="[2.6.0,2.7.0)", - org.apache.sshd.common.kex.extension;version="[2.6.0,2.7.0)", - org.apache.sshd.common.kex.extension.parser;version="[2.6.0,2.7.0)", - org.apache.sshd.common.keyprovider;version="[2.6.0,2.7.0)", - org.apache.sshd.common.mac;version="[2.6.0,2.7.0)", - org.apache.sshd.common.random;version="[2.6.0,2.7.0)", - org.apache.sshd.common.session;version="[2.6.0,2.7.0)", - org.apache.sshd.common.session.helpers;version="[2.6.0,2.7.0)", - org.apache.sshd.common.signature;version="[2.6.0,2.7.0)", - org.apache.sshd.common.util;version="[2.6.0,2.7.0)", - org.apache.sshd.common.util.buffer;version="[2.6.0,2.7.0)", - org.apache.sshd.common.util.closeable;version="[2.6.0,2.7.0)", - org.apache.sshd.common.util.io;version="[2.6.0,2.7.0)", - org.apache.sshd.common.util.io.resource;version="[2.6.0,2.7.0)", - org.apache.sshd.common.util.logging;version="[2.6.0,2.7.0)", - org.apache.sshd.common.util.net;version="[2.6.0,2.7.0)", - org.apache.sshd.common.util.security;version="[2.6.0,2.7.0)", - org.apache.sshd.core;version="[2.6.0,2.7.0)", - org.apache.sshd.server.auth;version="[2.6.0,2.7.0)", - org.apache.sshd.sftp;version="[2.6.0,2.7.0)", - org.apache.sshd.sftp.client;version="[2.6.0,2.7.0)", - org.apache.sshd.sftp.common;version="[2.6.0,2.7.0)", + org.apache.sshd.agent;version="[2.7.0,2.8.0)", + org.apache.sshd.client;version="[2.7.0,2.8.0)", + org.apache.sshd.client.auth;version="[2.7.0,2.8.0)", + org.apache.sshd.client.auth.keyboard;version="[2.7.0,2.8.0)", + org.apache.sshd.client.auth.password;version="[2.7.0,2.8.0)", + org.apache.sshd.client.auth.pubkey;version="[2.7.0,2.8.0)", + org.apache.sshd.client.channel;version="[2.7.0,2.8.0)", + org.apache.sshd.client.config.hosts;version="[2.7.0,2.8.0)", + org.apache.sshd.client.config.keys;version="[2.7.0,2.8.0)", + org.apache.sshd.client.future;version="[2.7.0,2.8.0)", + org.apache.sshd.client.keyverifier;version="[2.7.0,2.8.0)", + org.apache.sshd.client.session;version="[2.7.0,2.8.0)", + org.apache.sshd.client.session.forward;version="[2.7.0,2.8.0)", + org.apache.sshd.common;version="[2.7.0,2.8.0)", + org.apache.sshd.common.auth;version="[2.7.0,2.8.0)", + org.apache.sshd.common.channel;version="[2.7.0,2.8.0)", + org.apache.sshd.common.compression;version="[2.7.0,2.8.0)", + org.apache.sshd.common.config.keys;version="[2.7.0,2.8.0)", + org.apache.sshd.common.config.keys.loader;version="[2.7.0,2.8.0)", + org.apache.sshd.common.config.keys.loader.openssh.kdf;version="[2.7.0,2.8.0)", + org.apache.sshd.common.digest;version="[2.7.0,2.8.0)", + org.apache.sshd.common.forward;version="[2.7.0,2.8.0)", + org.apache.sshd.common.future;version="[2.7.0,2.8.0)", + org.apache.sshd.common.helpers;version="[2.7.0,2.8.0)", + org.apache.sshd.common.io;version="[2.7.0,2.8.0)", + org.apache.sshd.common.kex;version="[2.7.0,2.8.0)", + org.apache.sshd.common.kex.extension;version="[2.7.0,2.8.0)", + org.apache.sshd.common.kex.extension.parser;version="[2.7.0,2.8.0)", + org.apache.sshd.common.keyprovider;version="[2.7.0,2.8.0)", + org.apache.sshd.common.mac;version="[2.7.0,2.8.0)", + org.apache.sshd.common.random;version="[2.7.0,2.8.0)", + org.apache.sshd.common.session;version="[2.7.0,2.8.0)", + org.apache.sshd.common.session.helpers;version="[2.7.0,2.8.0)", + org.apache.sshd.common.signature;version="[2.7.0,2.8.0)", + org.apache.sshd.common.util;version="[2.7.0,2.8.0)", + org.apache.sshd.common.util.buffer;version="[2.7.0,2.8.0)", + org.apache.sshd.common.util.closeable;version="[2.7.0,2.8.0)", + org.apache.sshd.common.util.io;version="[2.7.0,2.8.0)", + org.apache.sshd.common.util.io.resource;version="[2.7.0,2.8.0)", + org.apache.sshd.common.util.logging;version="[2.7.0,2.8.0)", + org.apache.sshd.common.util.net;version="[2.7.0,2.8.0)", + org.apache.sshd.common.util.security;version="[2.7.0,2.8.0)", + org.apache.sshd.core;version="[2.7.0,2.8.0)", + org.apache.sshd.server.auth;version="[2.7.0,2.8.0)", + org.apache.sshd.sftp;version="[2.7.0,2.8.0)", + org.apache.sshd.sftp.client;version="[2.7.0,2.8.0)", + org.apache.sshd.sftp.common;version="[2.7.0,2.8.0)", org.eclipse.jgit.annotations;version="[5.13.0,5.14.0)", org.eclipse.jgit.errors;version="[5.13.0,5.14.0)", org.eclipse.jgit.fnmatch;version="[5.13.0,5.14.0)", diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java index f9e80121ed..7656fe8d08 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java @@ -136,8 +136,8 @@ public class JGitClientSession extends ClientSessionImpl { } @Override - protected IoWriteFuture sendIdentification(String ident) - throws IOException { + protected IoWriteFuture sendIdentification(String ident, + List extraLines) throws Exception { StatefulProxyConnector proxy = proxyHandler; if (proxy != null) { try { @@ -145,7 +145,8 @@ public class JGitClientSession extends ClientSessionImpl { // from the peer only once the initial sendKexInit() following // this call to sendIdentification() has returned! proxy.runWhenDone(() -> { - JGitClientSession.super.sendIdentification(ident); + JGitClientSession.super.sendIdentification(ident, + extraLines); return null; }); // Called only from the ClientSessionImpl constructor, where the @@ -157,12 +158,11 @@ public class JGitClientSession extends ClientSessionImpl { throw new IOException(other.getLocalizedMessage(), other); } } - return super.sendIdentification(ident); + return super.sendIdentification(ident, extraLines); } @Override - protected byte[] sendKexInit() - throws IOException, GeneralSecurityException { + protected byte[] sendKexInit() throws Exception { StatefulProxyConnector proxy = proxyHandler; if (proxy != null) { try { diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitKexExtensionHandler.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitKexExtensionHandler.java deleted file mode 100644 index 9446aaa7d6..0000000000 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitKexExtensionHandler.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2021 Thomas Wolf and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Distribution License v. 1.0 which is available at - * https://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: BSD-3-Clause - */ -package org.eclipse.jgit.internal.transport.sshd; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -import org.apache.sshd.common.AttributeRepository.AttributeKey; -import org.apache.sshd.common.NamedFactory; -import org.apache.sshd.common.kex.KexProposalOption; -import org.apache.sshd.common.kex.extension.KexExtensionHandler; -import org.apache.sshd.common.kex.extension.KexExtensions; -import org.apache.sshd.common.kex.extension.parser.ServerSignatureAlgorithms; -import org.apache.sshd.common.session.Session; -import org.apache.sshd.common.signature.Signature; -import org.apache.sshd.common.util.logging.AbstractLoggingBean; -import org.eclipse.jgit.util.StringUtils; - -/** - * Do not use the DefaultClientKexExtensionHandler from sshd; it doesn't work - * properly because of misconceptions. See SSHD-1141. - * - * @see SSHD-1141 - */ -public class JGitKexExtensionHandler extends AbstractLoggingBean - implements KexExtensionHandler { - - /** Singleton instance. */ - public static final JGitKexExtensionHandler INSTANCE = new JGitKexExtensionHandler(); - - /** - * Session {@link AttributeKey} used to store whether the extension - * indicator was already sent. - */ - private static final AttributeKey CLIENT_PROPOSAL_MADE = new AttributeKey<>(); - - /** - * Session {@link AttributeKey} storing the algorithms announced by the - * server as known. - */ - public static final AttributeKey> SERVER_ALGORITHMS = new AttributeKey<>(); - - private JGitKexExtensionHandler() { - // No public instantiation for singleton - } - - @Override - public boolean isKexExtensionsAvailable(Session session, - AvailabilityPhase phase) throws IOException { - return !AvailabilityPhase.PREKEX.equals(phase); - } - - @Override - public void handleKexInitProposal(Session session, boolean initiator, - Map proposal) throws IOException { - // If it's the very first time, we may add the marker telling the server - // that we are ready to handle SSH_MSG_EXT_INFO - if (session == null || session.isServerSession() || !initiator) { - return; - } - if (session.getAttribute(CLIENT_PROPOSAL_MADE) != null) { - return; - } - String kexAlgorithms = proposal.get(KexProposalOption.SERVERKEYS); - if (StringUtils.isEmptyOrNull(kexAlgorithms)) { - return; - } - List algorithms = new ArrayList<>(); - // We're a client. We mustn't send the server extension, and we should - // send the client extension only once. - for (String algo : kexAlgorithms.split(",")) { //$NON-NLS-1$ - if (KexExtensions.CLIENT_KEX_EXTENSION.equalsIgnoreCase(algo) - || KexExtensions.SERVER_KEX_EXTENSION - .equalsIgnoreCase(algo)) { - continue; - } - algorithms.add(algo); - } - // Tell the server that we want to receive SSH2_MSG_EXT_INFO - algorithms.add(KexExtensions.CLIENT_KEX_EXTENSION); - if (log.isDebugEnabled()) { - log.debug( - "handleKexInitProposal({}): proposing HostKeyAlgorithms {}", //$NON-NLS-1$ - session, algorithms); - } - proposal.put(KexProposalOption.SERVERKEYS, - String.join(",", algorithms)); //$NON-NLS-1$ - session.setAttribute(CLIENT_PROPOSAL_MADE, Boolean.TRUE); - } - - @Override - public boolean handleKexExtensionRequest(Session session, int index, - int count, String name, byte[] data) throws IOException { - if (ServerSignatureAlgorithms.NAME.equals(name)) { - handleServerSignatureAlgorithms(session, - ServerSignatureAlgorithms.INSTANCE.parseExtension(data)); - } - return true; - } - - /** - * Perform updates after a server-sig-algs extension has been received. - * - * @param session - * the message was received for - * @param serverAlgorithms - * signature algorithm names announced by the server - */ - protected void handleServerSignatureAlgorithms(Session session, - Collection serverAlgorithms) { - if (log.isDebugEnabled()) { - log.debug("handleServerSignatureAlgorithms({}): {}", session, //$NON-NLS-1$ - serverAlgorithms); - } - // Client determines order; server says what it supports. Re-order - // such that supported ones are at the front, in client order, - // followed by unsupported ones, also in client order. - if (serverAlgorithms != null && !serverAlgorithms.isEmpty()) { - List> clientAlgorithms = new ArrayList<>( - session.getSignatureFactories()); - if (log.isDebugEnabled()) { - log.debug( - "handleServerSignatureAlgorithms({}): PubkeyAcceptedAlgorithms before: {}", //$NON-NLS-1$ - session, clientAlgorithms); - } - List> unknown = new ArrayList<>(); - Set known = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); - known.addAll(serverAlgorithms); - for (Iterator> iter = clientAlgorithms - .iterator(); iter.hasNext();) { - NamedFactory algo = iter.next(); - if (!known.contains(algo.getName())) { - unknown.add(algo); - iter.remove(); - } - } - // Re-add the unknown ones at the end. Per RFC 8308, some - // servers may not announce _all_ their supported algorithms, - // and a client may use unknown algorithms. - clientAlgorithms.addAll(unknown); - if (log.isDebugEnabled()) { - log.debug( - "handleServerSignatureAlgorithms({}): PubkeyAcceptedAlgorithms after: {}", //$NON-NLS-1$ - session, clientAlgorithms); - } - session.setAttribute(SERVER_ALGORITHMS, known); - session.setSignatureFactories(clientAlgorithms); - } - } -} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java deleted file mode 100644 index 0e3e24dcff..0000000000 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2018, 2021 Thomas Wolf and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Distribution License v. 1.0 which is available at - * https://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: BSD-3-Clause - */ -package org.eclipse.jgit.internal.transport.sshd; - -import java.io.IOException; - -import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey; -import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory; -import org.apache.sshd.client.session.ClientSession; - -/** - * A customized authentication factory for public key user authentication. - */ -public class JGitPublicKeyAuthFactory extends UserAuthPublicKeyFactory { - - /** The singleton {@link JGitPublicKeyAuthFactory}. */ - public static final JGitPublicKeyAuthFactory FACTORY = new JGitPublicKeyAuthFactory(); - - private JGitPublicKeyAuthFactory() { - super(); - } - - @Override - public UserAuthPublicKey createUserAuth(ClientSession session) - throws IOException { - return new JGitPublicKeyAuthentication(getSignatureFactories()); - } -} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java deleted file mode 100644 index 6755094420..0000000000 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2018, 2021 Thomas Wolf and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Distribution License v. 1.0 which is available at - * https://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: BSD-3-Clause - */ -package org.eclipse.jgit.internal.transport.sshd; - -import java.io.IOException; -import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; -import java.text.MessageFormat; -import java.util.Deque; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey; -import org.apache.sshd.client.session.ClientSession; -import org.apache.sshd.common.NamedFactory; -import org.apache.sshd.common.NamedResource; -import org.apache.sshd.common.RuntimeSshException; -import org.apache.sshd.common.SshConstants; -import org.apache.sshd.common.config.keys.KeyUtils; -import org.apache.sshd.common.signature.Signature; -import org.apache.sshd.common.signature.SignatureFactoriesHolder; -import org.apache.sshd.common.util.buffer.Buffer; - -/** - * Custom {@link UserAuthPublicKey} implementation fixing SSHD-1105: if there - * are several signature algorithms applicable for a public key type, we must - * try them all, in the correct order. - * - * @see SSHD-1105 - * @see Bug - * 572056 - */ -public class JGitPublicKeyAuthentication extends UserAuthPublicKey { - - private final Deque currentAlgorithms = new LinkedList<>(); - - private String chosenAlgorithm; - - JGitPublicKeyAuthentication(List> factories) { - super(factories); - } - - @Override - protected boolean sendAuthDataRequest(ClientSession session, String service) - throws Exception { - if (current == null) { - currentAlgorithms.clear(); - chosenAlgorithm = null; - } - String currentAlgorithm = null; - if (current != null && !currentAlgorithms.isEmpty()) { - currentAlgorithm = currentAlgorithms.poll(); - if (chosenAlgorithm != null) { - Set knownServerAlgorithms = session.getAttribute( - JGitKexExtensionHandler.SERVER_ALGORITHMS); - if (knownServerAlgorithms != null - && knownServerAlgorithms.contains(chosenAlgorithm)) { - // We've tried key 'current' with 'chosenAlgorithm', but it - // failed. However, the server had told us it supported - // 'chosenAlgorithm'. Thus it makes no sense to continue - // with this key and other signature algorithms. Skip to the - // next key, if any. - currentAlgorithm = null; - } - } - } - if (currentAlgorithm == null) { - try { - if (keys == null || !keys.hasNext()) { - if (log.isDebugEnabled()) { - log.debug( - "sendAuthDataRequest({})[{}] no more keys to send", //$NON-NLS-1$ - session, service); - } - current = null; - return false; - } - current = keys.next(); - currentAlgorithms.clear(); - chosenAlgorithm = null; - } catch (Error e) { // Copied from superclass - throw new RuntimeSshException(e); - } - } - PublicKey key; - try { - key = current.getPublicKey(); - } catch (Error e) { // Copied from superclass - throw new RuntimeSshException(e); - } - if (currentAlgorithm == null) { - String keyType = KeyUtils.getKeyType(key); - Set aliases = new HashSet<>( - KeyUtils.getAllEquivalentKeyTypes(keyType)); - aliases.add(keyType); - List> existingFactories; - if (current instanceof SignatureFactoriesHolder) { - existingFactories = ((SignatureFactoriesHolder) current) - .getSignatureFactories(); - } else { - existingFactories = getSignatureFactories(); - } - if (existingFactories != null) { - if (log.isDebugEnabled()) { - log.debug( - "sendAuthDataRequest({})[{}] selecting from PubKeyAcceptedAlgorithms {}", //$NON-NLS-1$ - session, service, - NamedResource.getNames(existingFactories)); - } - // Select the factories by name and in order - existingFactories.forEach(f -> { - if (aliases.contains(f.getName())) { - currentAlgorithms.add(f.getName()); - } - }); - } - currentAlgorithm = currentAlgorithms.isEmpty() ? keyType - : currentAlgorithms.poll(); - } - String name = getName(); - if (log.isDebugEnabled()) { - log.debug( - "sendAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_REQUEST request {} type={} - fingerprint={}", //$NON-NLS-1$ - session, service, name, currentAlgorithm, - KeyUtils.getFingerPrint(key)); - } - - chosenAlgorithm = currentAlgorithm; - Buffer buffer = session - .createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST); - buffer.putString(session.getUsername()); - buffer.putString(service); - buffer.putString(name); - buffer.putBoolean(false); - buffer.putString(currentAlgorithm); - buffer.putPublicKey(key); - session.writePacket(buffer); - return true; - } - - @Override - protected boolean processAuthDataRequest(ClientSession session, - String service, Buffer buffer) throws Exception { - String name = getName(); - int cmd = buffer.getUByte(); - if (cmd != SshConstants.SSH_MSG_USERAUTH_PK_OK) { - throw new IllegalStateException(MessageFormat.format( - SshdText.get().pubkeyAuthWrongCommand, - SshConstants.getCommandMessageName(cmd), - session.getConnectAddress(), session.getServerVersion())); - } - PublicKey key; - try { - key = current.getPublicKey(); - } catch (Error e) { // Copied from superclass - throw new RuntimeSshException(e); - } - String rspKeyAlgorithm = buffer.getString(); - PublicKey rspKey = buffer.getPublicKey(); - if (log.isDebugEnabled()) { - log.debug( - "processAuthDataRequest({})[{}][{}] SSH_MSG_USERAUTH_PK_OK type={}, fingerprint={}", //$NON-NLS-1$ - session, service, name, rspKeyAlgorithm, - KeyUtils.getFingerPrint(rspKey)); - } - if (!KeyUtils.compareKeys(rspKey, key)) { - throw new InvalidKeySpecException(MessageFormat.format( - SshdText.get().pubkeyAuthWrongKey, - KeyUtils.getFingerPrint(key), - KeyUtils.getFingerPrint(rspKey), - session.getConnectAddress(), session.getServerVersion())); - } - if (!chosenAlgorithm.equalsIgnoreCase(rspKeyAlgorithm)) { - // 'algo' SHOULD be the same as 'chosenAlgorithm', which is the one - // we sent above. See https://tools.ietf.org/html/rfc4252#page-9 . - // - // However, at least Github (SSH-2.0-babeld-383743ad) servers seem - // to return the key type, not the algorithm name. - // - // So we don't check but just log the inconsistency. We sign using - // 'chosenAlgorithm' in any case, so we don't really care what the - // server says here. - log.warn(MessageFormat.format( - SshdText.get().pubkeyAuthWrongSignatureAlgorithm, - chosenAlgorithm, rspKeyAlgorithm, session.getConnectAddress(), - session.getServerVersion())); - } - String username = session.getUsername(); - Buffer out = session - .createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST); - out.putString(username); - out.putString(service); - out.putString(name); - out.putBoolean(true); - out.putString(chosenAlgorithm); - out.putPublicKey(key); - if (log.isDebugEnabled()) { - log.debug( - "processAuthDataRequest({})[{}][{}]: signing with algorithm {}", //$NON-NLS-1$ - session, service, name, chosenAlgorithm); - } - appendSignature(session, service, name, username, chosenAlgorithm, key, - out); - session.writePacket(out); - return true; - } - - @Override - protected void releaseKeys() throws IOException { - currentAlgorithms.clear(); - current = null; - chosenAlgorithm = null; - super.releaseKeys(); - } -} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java index 2d7e0c7c77..d52e24adfb 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java @@ -32,6 +32,7 @@ import org.apache.sshd.client.ClientBuilder; import org.apache.sshd.client.SshClient; import org.apache.sshd.client.auth.UserAuthFactory; import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory; +import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory; import org.apache.sshd.client.config.hosts.HostConfigEntryResolver; import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.SshException; @@ -47,9 +48,7 @@ import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile; import org.eclipse.jgit.internal.transport.sshd.AuthenticationCanceledException; import org.eclipse.jgit.internal.transport.sshd.CachingKeyPairProvider; import org.eclipse.jgit.internal.transport.sshd.GssApiWithMicAuthFactory; -import org.eclipse.jgit.internal.transport.sshd.JGitKexExtensionHandler; import org.eclipse.jgit.internal.transport.sshd.JGitPasswordAuthFactory; -import org.eclipse.jgit.internal.transport.sshd.JGitPublicKeyAuthFactory; import org.eclipse.jgit.internal.transport.sshd.JGitServerKeyVerifier; import org.eclipse.jgit.internal.transport.sshd.JGitSshClient; import org.eclipse.jgit.internal.transport.sshd.JGitSshConfig; @@ -217,7 +216,6 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable { new JGitUserInteraction(credentialsProvider)); client.setUserAuthFactories(getUserAuthFactories()); client.setKeyIdentityProvider(defaultKeysProvider); - client.setKexExtensionHandler(JGitKexExtensionHandler.INSTANCE); // JGit-specific things: JGitSshClient jgitClient = (JGitSshClient) client; jgitClient.setKeyCache(getKeyCache()); @@ -579,7 +577,7 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable { // Password auth doesn't have this problem. return Collections.unmodifiableList( Arrays.asList(GssApiWithMicAuthFactory.INSTANCE, - JGitPublicKeyAuthFactory.FACTORY, + UserAuthPublicKeyFactory.INSTANCE, JGitPasswordAuthFactory.INSTANCE, UserAuthKeyboardInteractiveFactory.INSTANCE)); } -- cgit v1.2.3 From 27a1fa1872da9d0da9147941aa6b372dee48cefb Mon Sep 17 00:00:00 2001 From: Thomas Wolf Date: Tue, 29 Jun 2021 22:57:09 +0200 Subject: [sshd] Implement SSH config KexAlgorithms Make the used KEX algorithms configurable via the ssh config. Also implement adding algorithms not in the default set: since sshd 2.6.0 deprecated SHA1-based algorithms, it is possible that the default set has not all available algorithms, so adding algorithms makes sense. This enables users who have to use a git server that only supports old SHA1-based key exchange methods to enable those methods in the ssh config: KexAlgorithms +diffie-hellman-group1-sha1 There are two more SHA1 algorithms that are not enabled by default: diffie-hellman-group14-sha1 and diffie-hellman-group-exchange-sha1. KeyAlgorithms accepts a comma-separated list of algorithm names. Since adding algorithms is now supported, adapt the handling of signature algorithms, too. Make sure that definitions for the KEX exchange signature (HostKeyAlgorithms) don't conflict with the definition for signatures for pubkey auth (PubkeyAcceptedAlgorithms). HostKeyAlgorithms updates the signature factories set on the session to include the default factories plus any that might have been added via the SSH config. Move the handling of PubkeyAcceptedAlgorithms from the client to the JGitPubkeyAuthentication, where it can be done only if pubkey auth is attempted at all and where it can store its adapted list of factories locally. Bug: 574636 Change-Id: Ia5d5f174bbc8e5b41e10ec2c25216d861174e7c3 Signed-off-by: Thomas Wolf --- .../META-INF/MANIFEST.MF | 1 + .../eclipse/jgit/transport/sshd/ApacheSshTest.java | 43 ++++++ .../internal/transport/sshd/SshdText.properties | 1 + .../internal/transport/sshd/JGitClientSession.java | 150 ++++++++++++++++++--- .../transport/sshd/JGitPublicKeyAuthFactory.java | 35 +++++ .../sshd/JGitPublicKeyAuthentication.java | 64 +++++++++ .../internal/transport/sshd/JGitSshClient.java | 20 +-- .../jgit/internal/transport/sshd/SshdText.java | 1 + .../jgit/transport/sshd/SshdSessionFactory.java | 4 +- 9 files changed, 282 insertions(+), 37 deletions(-) create mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java create mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java (limited to 'org.eclipse.jgit.ssh.apache') diff --git a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF index ddb475dc13..1ac8faaa41 100644 --- a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF @@ -12,6 +12,7 @@ Import-Package: org.apache.sshd.client.config.hosts;version="[2.7.0,2.8.0)", org.apache.sshd.common.auth;version="[2.7.0,2.8.0)", org.apache.sshd.common.config.keys;version="[2.7.0,2.8.0)", org.apache.sshd.common.helpers;version="[2.7.0,2.8.0)", + org.apache.sshd.common.kex;version="[2.7.0,2.8.0)", org.apache.sshd.common.keyprovider;version="[2.7.0,2.8.0)", org.apache.sshd.common.session;version="[2.7.0,2.8.0)", org.apache.sshd.common.signature;version="[2.7.0,2.8.0)", diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java index c56d2307c6..c1f5fef3cd 100644 --- a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java +++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java @@ -34,13 +34,18 @@ import java.util.stream.Collectors; import org.apache.sshd.client.config.hosts.KnownHostEntry; import org.apache.sshd.client.config.hosts.KnownHostHashValue; +import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.config.keys.PublicKeyEntry; import org.apache.sshd.common.config.keys.PublicKeyEntryResolver; +import org.apache.sshd.common.kex.BuiltinDHFactories; +import org.apache.sshd.common.kex.DHFactory; +import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.util.net.SshdSocketAddress; import org.apache.sshd.server.ServerAuthenticationManager; +import org.apache.sshd.server.ServerBuilder; import org.apache.sshd.server.SshServer; import org.apache.sshd.server.forward.StaticDecisionForwardingFilter; import org.eclipse.jgit.api.Git; @@ -702,4 +707,42 @@ public class ApacheSshTest extends SshTestBase { session.disconnect(); } } + + /** + * Tests that one can log in at an even poorer server that also only has the + * SHA1 KEX methods available. Apparently this is the case for at least some + * Microsoft TFS instances. The user has to enable the poor KEX methods in + * the ssh config explicitly; we don't enable them by default. + * + * @throws Exception + * on failure + */ + @Test + public void testConnectOnlyRsaSha1() throws Exception { + try (SshServer oldServer = createServer(TEST_USER, publicKey1)) { + oldServer.setSignatureFactoriesNames("ssh-rsa"); + List sha1Factories = BuiltinDHFactories + .parseDHFactoriesList( + "diffie-hellman-group1-sha1,diffie-hellman-group14-sha1") + .getParsedFactories(); + assertEquals(2, sha1Factories.size()); + List kexFactories = NamedFactory + .setUpTransformedFactories(true, sha1Factories, + ServerBuilder.DH2KEX); + oldServer.setKeyExchangeFactories(kexFactories); + oldServer.start(); + registerServer(oldServer); + installConfig("Host server", // + "HostName localhost", // + "Port " + oldServer.getPort(), // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath(), // + "KexAlgorithms +diffie-hellman-group1-sha1"); + RemoteSession session = getSessionFactory().getSession( + new URIish("ssh://server/doesntmatter"), null, FS.DETECTED, + 10000); + assertNotNull(session); + session.disconnect(); + } + } } diff --git a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties index 5bc0867674..defcbdcfc1 100644 --- a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties +++ b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties @@ -8,6 +8,7 @@ configInvalidProxyJump=Ssh config, host ''{0}'': Cannot parse ProxyJump ''{1}'' configNoKnownAlgorithms=Ssh config ''{0}'' ''{1}'' resulted in empty list (none known, or all known removed); using default. configProxyJumpNotSsh=Non-ssh URI in ProxyJump ssh config configProxyJumpWithPath=ProxyJump ssh config: jump host specification must not have a path +configUnknownAlgorithm=Ssh config {0}: ignoring unknown algorithm ''{1}'' in {2} {3} ftpCloseFailed=Closing the SFTP channel failed gssapiFailure=GSS-API error for mechanism OID {0} gssapiInitFailure=GSS-API initialization failure for mechanism {0} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java index 7656fe8d08..066cec38ba 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, 2019 Thomas Wolf and others + * Copyright (C) 2018, 2021 Thomas Wolf and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -29,17 +29,26 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import org.apache.sshd.client.ClientBuilder; import org.apache.sshd.client.ClientFactoryManager; import org.apache.sshd.client.config.hosts.HostConfigEntry; import org.apache.sshd.client.keyverifier.ServerKeyVerifier; import org.apache.sshd.client.session.ClientSessionImpl; import org.apache.sshd.common.AttributeRepository; import org.apache.sshd.common.FactoryManager; +import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.PropertyResolver; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.io.IoSession; import org.apache.sshd.common.io.IoWriteFuture; +import org.apache.sshd.common.kex.BuiltinDHFactories; +import org.apache.sshd.common.kex.DHFactory; import org.apache.sshd.common.kex.KexProposalOption; +import org.apache.sshd.common.kex.KeyExchangeFactory; +import org.apache.sshd.common.kex.extension.KexExtensionHandler; +import org.apache.sshd.common.kex.extension.KexExtensions; +import org.apache.sshd.common.signature.BuiltinSignatures; +import org.apache.sshd.common.kex.extension.KexExtensionHandler.AvailabilityPhase; import org.apache.sshd.common.util.Readable; import org.apache.sshd.common.util.buffer.Buffer; import org.eclipse.jgit.errors.InvalidPatternException; @@ -70,6 +79,8 @@ public class JGitClientSession extends ClientSessionImpl { */ private static final int DEFAULT_MAX_IDENTIFICATION_SIZE = 64 * 1024; + private static final AttributeKey INITIAL_KEX_DONE = new AttributeKey<>(); + private HostConfigEntry hostConfig; private CredentialsProvider credentialsProvider; @@ -219,6 +230,32 @@ public class JGitClientSession extends ClientSessionImpl { return result; } + Set getAllAvailableSignatureAlgorithms() { + Set allAvailable = new HashSet<>(); + BuiltinSignatures.VALUES.forEach(s -> allAvailable.add(s.getName())); + BuiltinSignatures.getRegisteredExtensions() + .forEach(s -> allAvailable.add(s.getName())); + return allAvailable; + } + + private void setNewFactories(Collection defaultFactories, + Collection finalFactories) { + // If new factory names were added make sure we actually have factories + // for them all. + // + // But add new ones at the end: we don't want to change the order for + // pubkey auth, and any new ones added here were not included in the + // default set for some reason, such as being deprecated or weak. + // + // The order for KEX is determined by the order in the proposal string, + // but the order in pubkey auth is determined by the order in the + // factory list (possibly overridden via ssh config + // PubkeyAcceptedAlgorithms; see JGitPublicKeyAuthentication). + Set resultSet = new LinkedHashSet<>(defaultFactories); + resultSet.addAll(finalFactories); + setSignatureFactoriesNames(resultSet); + } + @Override protected String resolveAvailableSignaturesProposal( FactoryManager manager) { @@ -229,16 +266,17 @@ public class JGitClientSession extends ClientSessionImpl { .getProperty(SshConstants.HOST_KEY_ALGORITHMS); if (!StringUtils.isEmptyOrNull(algorithms)) { List result = modifyAlgorithmList(defaultSignatures, - algorithms, SshConstants.HOST_KEY_ALGORITHMS); + getAllAvailableSignatureAlgorithms(), algorithms, + SshConstants.HOST_KEY_ALGORITHMS); if (!result.isEmpty()) { if (log.isDebugEnabled()) { log.debug(SshConstants.HOST_KEY_ALGORITHMS + ' ' + result); } + setNewFactories(defaultSignatures, result); return String.join(",", result); //$NON-NLS-1$ } log.warn(format(SshdText.get().configNoKnownAlgorithms, - SshConstants.HOST_KEY_ALGORITHMS, - algorithms)); + SshConstants.HOST_KEY_ALGORITHMS, algorithms)); } // No HostKeyAlgorithms; using default -- change order to put existing // keys first. @@ -261,6 +299,10 @@ public class JGitClientSession extends ClientSessionImpl { if (log.isDebugEnabled()) { log.debug(SshConstants.HOST_KEY_ALGORITHMS + ' ' + reordered); } + // Make sure we actually have factories for them all. + if (reordered.size() > defaultSignatures.size()) { + setNewFactories(defaultSignatures, reordered); + } return String.join(",", reordered); //$NON-NLS-1$ } if (log.isDebugEnabled()) { @@ -270,15 +312,87 @@ public class JGitClientSession extends ClientSessionImpl { return String.join(",", defaultSignatures); //$NON-NLS-1$ } + private List determineKexProposal() { + List kexFactories = getKeyExchangeFactories(); + List defaultKexMethods = NamedResource + .getNameList(kexFactories); + HostConfigEntry config = resolveAttribute( + JGitSshClient.HOST_CONFIG_ENTRY); + String algorithms = config.getProperty(SshConstants.KEX_ALGORITHMS); + if (!StringUtils.isEmptyOrNull(algorithms)) { + Set allAvailable = new HashSet<>(); + BuiltinDHFactories.VALUES + .forEach(s -> allAvailable.add(s.getName())); + BuiltinDHFactories.getRegisteredExtensions() + .forEach(s -> allAvailable.add(s.getName())); + List result = modifyAlgorithmList(defaultKexMethods, + allAvailable, algorithms, SshConstants.KEX_ALGORITHMS); + if (!result.isEmpty()) { + // If new ones were added, update the installed factories + Set configuredKexMethods = new HashSet<>( + defaultKexMethods); + List newKexFactories = new ArrayList<>(); + result.forEach(name -> { + if (!configuredKexMethods.contains(name)) { + DHFactory factory = BuiltinDHFactories + .resolveFactory(name); + if (factory == null) { + // Should not occur here + if (log.isDebugEnabled()) { + log.debug( + "determineKexProposal({}) unknown KEX algorithm {} ignored", //$NON-NLS-1$ + this, name); + } + } else { + newKexFactories + .add(ClientBuilder.DH2KEX.apply(factory)); + } + } + }); + if (!newKexFactories.isEmpty()) { + newKexFactories.addAll(kexFactories); + setKeyExchangeFactories(newKexFactories); + } + return result; + } + log.warn(format(SshdText.get().configNoKnownAlgorithms, + SshConstants.KEX_ALGORITHMS, algorithms)); + } + return defaultKexMethods; + } + + @Override + protected String resolveSessionKexProposal(String hostKeyTypes) + throws IOException { + String kexMethods = String.join(",", determineKexProposal()); //$NON-NLS-1$ + Boolean isRekey = getAttribute(INITIAL_KEX_DONE); + if (isRekey == null || !isRekey.booleanValue()) { + // First time + KexExtensionHandler extHandler = getKexExtensionHandler(); + if (extHandler != null && extHandler.isKexExtensionsAvailable(this, + AvailabilityPhase.PROPOSAL)) { + if (kexMethods.isEmpty()) { + kexMethods = KexExtensions.CLIENT_KEX_EXTENSION; + } else { + kexMethods += ',' + KexExtensions.CLIENT_KEX_EXTENSION; + } + } + setAttribute(INITIAL_KEX_DONE, Boolean.TRUE); + } + if (log.isDebugEnabled()) { + log.debug(SshConstants.KEX_ALGORITHMS + ' ' + kexMethods); + } + return kexMethods; + } + /** * Modifies a given algorithm list according to a list from the ssh config, - * including remove ('-') and reordering ('^') operators. Addition ('+') is - * not handled since we have no way of adding dynamically implementations, - * and the defaultList is supposed to contain all known implementations - * already. + * including add ('+'), remove ('-') and reordering ('^') operators. * * @param defaultList * to modify + * @param allAvailable + * all available values * @param fromConfig * telling how to modify the {@code defaultList}, must not be * {@code null} or empty @@ -288,22 +402,22 @@ public class JGitClientSession extends ClientSessionImpl { * set */ public List modifyAlgorithmList(List defaultList, - String fromConfig, String overrideKey) { + Set allAvailable, String fromConfig, String overrideKey) { Set defaults = new LinkedHashSet<>(); defaults.addAll(defaultList); switch (fromConfig.charAt(0)) { case '+': - // Additions make not much sense -- it's either in - // defaultList already, or we have no implementation for - // it. No point in proposing it. - return defaultList; + List newSignatures = filteredList(allAvailable, overrideKey, + fromConfig.substring(1)); + defaults.addAll(newSignatures); + return new ArrayList<>(defaults); case '-': // This takes wildcard patterns! removeFromList(defaults, overrideKey, fromConfig.substring(1)); return new ArrayList<>(defaults); case '^': // Specified entries go to the front of the default list - List allSignatures = filteredList(defaults, + List allSignatures = filteredList(allAvailable, overrideKey, fromConfig.substring(1)); Set atFront = new HashSet<>(allSignatures); for (String sig : defaults) { @@ -315,7 +429,7 @@ public class JGitClientSession extends ClientSessionImpl { default: // Default is overridden -- only accept the ones for which we do // have an implementation. - return filteredList(defaults, fromConfig); + return filteredList(allAvailable, overrideKey, fromConfig); } } @@ -342,11 +456,15 @@ public class JGitClientSession extends ClientSessionImpl { } } - private List filteredList(Set known, String values) { + private List filteredList(Set known, String key, + String values) { List newNames = new ArrayList<>(); for (String newValue : values.split("\\s*,\\s*")) { //$NON-NLS-1$ if (known.contains(newValue)) { newNames.add(newValue); + } else { + log.warn(format(SshdText.get().configUnknownAlgorithm, this, + newValue, key, values)); } } return newNames; diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java new file mode 100644 index 0000000000..0e3e24dcff --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2018, 2021 Thomas Wolf and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.internal.transport.sshd; + +import java.io.IOException; + +import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey; +import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory; +import org.apache.sshd.client.session.ClientSession; + +/** + * A customized authentication factory for public key user authentication. + */ +public class JGitPublicKeyAuthFactory extends UserAuthPublicKeyFactory { + + /** The singleton {@link JGitPublicKeyAuthFactory}. */ + public static final JGitPublicKeyAuthFactory FACTORY = new JGitPublicKeyAuthFactory(); + + private JGitPublicKeyAuthFactory() { + super(); + } + + @Override + public UserAuthPublicKey createUserAuth(ClientSession session) + throws IOException { + return new JGitPublicKeyAuthentication(getSignatureFactories()); + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java new file mode 100644 index 0000000000..08da18f5aa --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2018, 2021 Thomas Wolf and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.internal.transport.sshd; + +import static java.text.MessageFormat.format; +import static org.eclipse.jgit.transport.SshConstants.PUBKEY_ACCEPTED_ALGORITHMS; + +import java.util.List; + +import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey; +import org.apache.sshd.client.config.hosts.HostConfigEntry; +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.signature.Signature; +import org.eclipse.jgit.util.StringUtils; + +/** + * Custom {@link UserAuthPublicKey} implementation for handling SSH config + * PubkeyAcceptedAlgorithms. + */ +public class JGitPublicKeyAuthentication extends UserAuthPublicKey { + + JGitPublicKeyAuthentication(List> factories) { + super(factories); + } + + @Override + public void init(ClientSession rawSession, String service) + throws Exception { + if (!(rawSession instanceof JGitClientSession)) { + throw new IllegalStateException("Wrong session type: " //$NON-NLS-1$ + + rawSession.getClass().getCanonicalName()); + } + JGitClientSession session = ((JGitClientSession) rawSession); + HostConfigEntry hostConfig = session.getHostConfigEntry(); + // Set signature algorithms for public key authentication + String pubkeyAlgos = hostConfig.getProperty(PUBKEY_ACCEPTED_ALGORITHMS); + if (!StringUtils.isEmptyOrNull(pubkeyAlgos)) { + List signatures = session.getSignatureFactoriesNames(); + signatures = session.modifyAlgorithmList(signatures, + session.getAllAvailableSignatureAlgorithms(), pubkeyAlgos, + PUBKEY_ACCEPTED_ALGORITHMS); + if (!signatures.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug(PUBKEY_ACCEPTED_ALGORITHMS + ' ' + signatures); + } + setSignatureFactoriesNames(signatures); + } else { + log.warn(format(SshdText.get().configNoKnownAlgorithms, + PUBKEY_ACCEPTED_ALGORITHMS, pubkeyAlgos)); + } + } + // If we don't set signature factories here, the default ones from the + // session will be used. + super.init(session, service); + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java index 071e1979d3..ae12c2028d 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, 2020 Thomas Wolf and others + * Copyright (C) 2018, 2021 Thomas Wolf and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -267,24 +267,6 @@ public class JGitSshClient extends SshClient { session.setUsername(username); session.setConnectAddress(address); session.setHostConfigEntry(hostConfig); - // Set signature algorithms for public key authentication - String pubkeyAlgos = hostConfig - .getProperty(SshConstants.PUBKEY_ACCEPTED_ALGORITHMS); - if (!StringUtils.isEmptyOrNull(pubkeyAlgos)) { - List signatures = getSignatureFactoriesNames(); - signatures = session.modifyAlgorithmList(signatures, pubkeyAlgos, - SshConstants.PUBKEY_ACCEPTED_ALGORITHMS); - if (!signatures.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug(SshConstants.PUBKEY_ACCEPTED_ALGORITHMS + ' ' - + signatures); - } - session.setSignatureFactoriesNames(signatures); - } else { - log.warn(format(SshdText.get().configNoKnownAlgorithms, - SshConstants.PUBKEY_ACCEPTED_ALGORITHMS, pubkeyAlgos)); - } - } if (session.getCredentialsProvider() == null) { session.setCredentialsProvider(getCredentialsProvider()); } diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java index 73c2288ccc..c0f5719629 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java @@ -28,6 +28,7 @@ public final class SshdText extends TranslationBundle { /***/ public String configNoKnownAlgorithms; /***/ public String configProxyJumpNotSsh; /***/ public String configProxyJumpWithPath; + /***/ public String configUnknownAlgorithm; /***/ public String ftpCloseFailed; /***/ public String gssapiFailure; /***/ public String gssapiInitFailure; diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java index d52e24adfb..cad959c904 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java @@ -32,7 +32,6 @@ import org.apache.sshd.client.ClientBuilder; import org.apache.sshd.client.SshClient; import org.apache.sshd.client.auth.UserAuthFactory; import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory; -import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory; import org.apache.sshd.client.config.hosts.HostConfigEntryResolver; import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.SshException; @@ -49,6 +48,7 @@ import org.eclipse.jgit.internal.transport.sshd.AuthenticationCanceledException; import org.eclipse.jgit.internal.transport.sshd.CachingKeyPairProvider; import org.eclipse.jgit.internal.transport.sshd.GssApiWithMicAuthFactory; import org.eclipse.jgit.internal.transport.sshd.JGitPasswordAuthFactory; +import org.eclipse.jgit.internal.transport.sshd.JGitPublicKeyAuthFactory; import org.eclipse.jgit.internal.transport.sshd.JGitServerKeyVerifier; import org.eclipse.jgit.internal.transport.sshd.JGitSshClient; import org.eclipse.jgit.internal.transport.sshd.JGitSshConfig; @@ -577,7 +577,7 @@ public class SshdSessionFactory extends SshSessionFactory implements Closeable { // Password auth doesn't have this problem. return Collections.unmodifiableList( Arrays.asList(GssApiWithMicAuthFactory.INSTANCE, - UserAuthPublicKeyFactory.INSTANCE, + JGitPublicKeyAuthFactory.FACTORY, JGitPasswordAuthFactory.INSTANCE, UserAuthKeyboardInteractiveFactory.INSTANCE)); } -- cgit v1.2.3 From 4c5c3e9fb8e6f11cee3245a4b374a96b9a4f0ee4 Mon Sep 17 00:00:00 2001 From: Thomas Wolf Date: Tue, 29 Jun 2021 23:08:02 +0200 Subject: [sshd] Distinguish key type and signature algorithm for host key Since the introduction of the rsa-sha2-512 and rsa-sha2-256 signature types, the key type for RSA is no longer automatically the signature algorithm. We re-order the list for the host key proposal such that keys we already have are preferred; this minimizes warnings about new host keys. When doing so, put all of rsa-sha2-512, rsa-sha2-256, and ssh-rsa at the front, in that order, not just ssh-rsa. This ensures that we do prefer RSA keys if we already have an RSA host key, but at the same time we still prefer the stronger signature algorithms over the weaker and deprecated SHA1-based ssh-rsa signature. It also helps avoid a bug found in some Github versions where the Github SSH server uses a rsa-sha2-512 signature even though ssh-rsa was negotiated.[1] [1] https://www.eclipse.org/forums/index.php/t/1108282/ Bug: 574635 Change-Id: I0a49dcfa0c2c93f23118c983cd0bc9e5a467d886 Signed-off-by: Thomas Wolf --- .../org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'org.eclipse.jgit.ssh.apache') diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java index 066cec38ba..2133a29ccc 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java @@ -47,6 +47,7 @@ import org.apache.sshd.common.kex.KexProposalOption; import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.kex.extension.KexExtensionHandler; import org.apache.sshd.common.kex.extension.KexExtensions; +import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.signature.BuiltinSignatures; import org.apache.sshd.common.kex.extension.KexExtensionHandler.AvailabilityPhase; import org.apache.sshd.common.util.Readable; @@ -291,6 +292,11 @@ public class JGitClientSession extends ClientSessionImpl { if (key != null) { String keyType = KeyUtils.getKeyType(key); if (keyType != null) { + if (KeyPairProvider.SSH_RSA.equals(keyType)) { + // Add all available signatures for ssh-rsa. + reordered.add(KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS); + reordered.add(KeyUtils.RSA_SHA256_KEY_TYPE_ALIAS); + } reordered.add(keyType); } } -- cgit v1.2.3 From 13777a3a6265ee68966547e69de83410e0621dfc Mon Sep 17 00:00:00 2001 From: Thomas Wolf Date: Tue, 18 May 2021 21:44:18 +0200 Subject: [sshd] Ignore revoked keys in OpenSshServerKeyDatabase.lookup() It makes no sense to return revoked keys. Change-Id: I99eee1de3dba5c0c8d275b7c1a24053874b3cb03 Signed-off-by: Thomas Wolf --- .../transport/sshd/OpenSshServerKeyDatabase.java | 25 ++++++++++++++-------- .../jgit/transport/sshd/ServerKeyDatabase.java | 4 ++-- 2 files changed, 18 insertions(+), 11 deletions(-) (limited to 'org.eclipse.jgit.ssh.apache') diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java index 1a530b7743..85e406f422 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, 2019 Thomas Wolf and others + * Copyright (C) 2018, 2021 Thomas Wolf and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -182,10 +182,13 @@ public class OpenSshServerKeyDatabase for (HostKeyFile file : filesToUse) { for (HostEntryPair current : file.get()) { KnownHostEntry entry = current.getHostEntry(); - for (SshdSocketAddress host : candidates) { - if (entry.isHostMatch(host.getHostName(), host.getPort())) { - result.add(current.getServerKey()); - break; + if (!isRevoked(entry)) { + for (SshdSocketAddress host : candidates) { + if (entry.isHostMatch(host.getHostName(), + host.getPort())) { + result.add(current.getServerKey()); + break; + } } } } @@ -266,6 +269,10 @@ public class OpenSshServerKeyDatabase private static final long serialVersionUID = 1L; } + private boolean isRevoked(KnownHostEntry entry) { + return MARKER_REVOKED.equals(entry.getMarker()); + } + private boolean find(Collection candidates, PublicKey serverKey, List entries, HostEntryPair[] modified) throws RevokedKeyException { @@ -273,22 +280,22 @@ public class OpenSshServerKeyDatabase KnownHostEntry entry = current.getHostEntry(); for (SshdSocketAddress host : candidates) { if (entry.isHostMatch(host.getHostName(), host.getPort())) { - boolean isRevoked = MARKER_REVOKED - .equals(entry.getMarker()); + boolean revoked = isRevoked(entry); if (KeyUtils.compareKeys(serverKey, current.getServerKey())) { // Exact match - if (isRevoked) { + if (revoked) { throw new RevokedKeyException(); } modified[0] = null; return true; - } else if (!isRevoked) { + } else if (!revoked) { // Server sent a different key modified[0] = current; // Keep going -- maybe there's another entry for this // host } + break; } } } diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ServerKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ServerKeyDatabase.java index b8e6cfd14d..b1b3c1808a 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ServerKeyDatabase.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ServerKeyDatabase.java @@ -30,7 +30,7 @@ import org.eclipse.jgit.transport.CredentialsProvider; public interface ServerKeyDatabase { /** - * Retrieves all known host keys for the given addresses. + * Retrieves all known and not revoked host keys for the given addresses. * * @param connectAddress * IP address the session tried to connect to @@ -39,7 +39,7 @@ public interface ServerKeyDatabase { * @param config * giving access to potentially interesting configuration * settings - * @return the list of known keys for the given addresses + * @return the list of known and not revoked keys for the given addresses */ @NonNull List lookup(@NonNull String connectAddress, -- cgit v1.2.3 From cbb9188cdab672d5cc6f8632a3c64b837a29ebdd Mon Sep 17 00:00:00 2001 From: Thomas Wolf Date: Wed, 21 Jul 2021 19:53:47 +0200 Subject: [sshd] Minor code clean-up Since upstream changed the method profile to throw Exception it's not necessary anymore to re-throw as IOException. Change-Id: I31afab4f6e1a2f0feef79e6abced20d0ca1c493b --- .../internal/transport/sshd/JGitClientSession.java | 52 ++++++++-------------- 1 file changed, 19 insertions(+), 33 deletions(-) (limited to 'org.eclipse.jgit.ssh.apache') diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java index 2133a29ccc..f7b37d7816 100644 --- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.io.StreamCorruptedException; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; import java.security.PublicKey; import java.util.ArrayList; import java.util.Collection; @@ -46,10 +45,10 @@ import org.apache.sshd.common.kex.DHFactory; import org.apache.sshd.common.kex.KexProposalOption; import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.kex.extension.KexExtensionHandler; +import org.apache.sshd.common.kex.extension.KexExtensionHandler.AvailabilityPhase; import org.apache.sshd.common.kex.extension.KexExtensions; import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.signature.BuiltinSignatures; -import org.apache.sshd.common.kex.extension.KexExtensionHandler.AvailabilityPhase; import org.apache.sshd.common.util.Readable; import org.apache.sshd.common.util.buffer.Buffer; import org.eclipse.jgit.errors.InvalidPatternException; @@ -152,23 +151,16 @@ public class JGitClientSession extends ClientSessionImpl { List extraLines) throws Exception { StatefulProxyConnector proxy = proxyHandler; if (proxy != null) { - try { - // We must not block here; the framework starts reading messages - // from the peer only once the initial sendKexInit() following - // this call to sendIdentification() has returned! - proxy.runWhenDone(() -> { - JGitClientSession.super.sendIdentification(ident, - extraLines); - return null; - }); - // Called only from the ClientSessionImpl constructor, where the - // return value is ignored. + // We must not block here; the framework starts reading messages + // from the peer only once the initial sendKexInit() following + // this call to sendIdentification() has returned! + proxy.runWhenDone(() -> { + JGitClientSession.super.sendIdentification(ident, extraLines); return null; - } catch (IOException e) { - throw e; - } catch (Exception other) { - throw new IOException(other.getLocalizedMessage(), other); - } + }); + // Called only from the ClientSessionImpl constructor, where the + // return value is ignored. + return null; } return super.sendIdentification(ident, extraLines); } @@ -177,22 +169,16 @@ public class JGitClientSession extends ClientSessionImpl { protected byte[] sendKexInit() throws Exception { StatefulProxyConnector proxy = proxyHandler; if (proxy != null) { - try { - // We must not block here; the framework starts reading messages - // from the peer only once the initial sendKexInit() has - // returned! - proxy.runWhenDone(() -> { - JGitClientSession.super.sendKexInit(); - return null; - }); - // This is called only from the ClientSessionImpl - // constructor, where the return value is ignored. + // We must not block here; the framework starts reading messages + // from the peer only once the initial sendKexInit() has + // returned! + proxy.runWhenDone(() -> { + JGitClientSession.super.sendKexInit(); return null; - } catch (IOException | GeneralSecurityException e) { - throw e; - } catch (Exception other) { - throw new IOException(other.getLocalizedMessage(), other); - } + }); + // This is called only from the ClientSessionImpl + // constructor, where the return value is ignored. + return null; } return super.sendKexInit(); } -- cgit v1.2.3