From e58d2f1e1dc4b4e93eb5cb076aefbe343b14648a Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Mon, 22 Jul 2024 18:26:32 +0200 Subject: [PATCH 1/2] Support for new client side extension --- .../DefaultClientKexExtensionHandler.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/extension/DefaultClientKexExtensionHandler.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/extension/DefaultClientKexExtensionHandler.java index f70128e40..a0924c559 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/kex/extension/DefaultClientKexExtensionHandler.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/extension/DefaultClientKexExtensionHandler.java @@ -23,9 +23,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.function.BiConsumer; import org.apache.sshd.common.AttributeRepository.AttributeKey; import org.apache.sshd.common.NamedFactory; @@ -33,6 +36,7 @@ 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.buffer.Buffer; import org.apache.sshd.common.util.logging.AbstractLoggingBean; /** @@ -133,4 +137,33 @@ protected void handleServerSignatureAlgorithms(Session session, Collection extensions = new LinkedHashMap<>(); + collectExtensions(session, phase, extensions::put); + if (!extensions.isEmpty()) { + Buffer buffer = session.createBuffer(KexExtensions.SSH_MSG_EXT_INFO); + KexExtensions.putExtensions(extensions.entrySet(), buffer); + if (log.isDebugEnabled()) { + log.debug("sendKexExtensions({})[{}]: sending SSH_MSG_EXT_INFO with {} info records", session, phase, + extensions.size()); + } + session.writePacket(buffer); + } + } + + /** + * Collects extension info records, handing them off to the given {@code marshaller} for writing into an + * {@link KexExtensions#SSH_MSG_EXT_INFO} message. + *

+ * This default implementation does not marshal any extension. + *

+ * + * @param session {@link Session} to send the KEX extension information for + * @param phase {@link KexPhase} of the SSH protocol + * @param marshaller {@link BiConsumer} writing the extensions into an SSH message + */ + public void collectExtensions(Session session, KexPhase phase, BiConsumer marshaller) { + } } From 4da7f419e465455412af64d24a006139d02b8f31 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 25 Jul 2024 12:39:58 +0200 Subject: [PATCH 2/2] [GH-545] Implements global-requests-ok extension (fixes #545) --- .../common/kex/extension/KexExtensions.java | 4 +- .../extension/parser/GlobalRequestsOk.java | 55 +++++++++++++++++++ .../DefaultClientKexExtensionHandler.java | 7 ++- .../DefaultServerKexExtensionHandler.java | 14 +++++ 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 sshd-common/src/main/java/org/apache/sshd/common/kex/extension/parser/GlobalRequestsOk.java diff --git a/sshd-common/src/main/java/org/apache/sshd/common/kex/extension/KexExtensions.java b/sshd-common/src/main/java/org/apache/sshd/common/kex/extension/KexExtensions.java index f275227e1..ddb3d8b69 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/kex/extension/KexExtensions.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/kex/extension/KexExtensions.java @@ -39,6 +39,7 @@ import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.kex.extension.parser.DelayCompression; import org.apache.sshd.common.kex.extension.parser.Elevation; +import org.apache.sshd.common.kex.extension.parser.GlobalRequestsOk; import org.apache.sshd.common.kex.extension.parser.NoFlowControl; import org.apache.sshd.common.kex.extension.parser.ServerSignatureAlgorithms; import org.apache.sshd.common.util.GenericUtils; @@ -84,7 +85,8 @@ public final class KexExtensions { ServerSignatureAlgorithms.INSTANCE, NoFlowControl.INSTANCE, Elevation.INSTANCE, - DelayCompression.INSTANCE) + DelayCompression.INSTANCE, + GlobalRequestsOk.INSTANCE) .collect(Collectors.toMap( NamedResource::getName, Function.identity(), MapEntryUtils.throwingMerger(), () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER))); diff --git a/sshd-common/src/main/java/org/apache/sshd/common/kex/extension/parser/GlobalRequestsOk.java b/sshd-common/src/main/java/org/apache/sshd/common/kex/extension/parser/GlobalRequestsOk.java new file mode 100644 index 000000000..20364c9db --- /dev/null +++ b/sshd-common/src/main/java/org/apache/sshd/common/kex/extension/parser/GlobalRequestsOk.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sshd.common.kex.extension.parser; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.apache.sshd.common.util.buffer.Buffer; + +/** + * @author Apache MINA SSHD Project + * @see draft-ssh-global-requests-ok-00 + */ +public class GlobalRequestsOk extends AbstractKexExtensionParser { + + public static final String NAME = "global-requests-ok"; + + public static final GlobalRequestsOk INSTANCE = new GlobalRequestsOk(); + + public GlobalRequestsOk() { + super(NAME); + } + + @Override + protected void encode(String value, Buffer buffer) throws IOException { + buffer.putString(value); + } + + @Override + public String parseExtension(byte[] data, int off, int len) throws IOException { + return (len <= 0) ? "" : new String(data, off, len, StandardCharsets.UTF_8); + } + + @Override + public String parseExtension(Buffer buffer) throws IOException { + return parseExtension(buffer.array(), buffer.rpos(), buffer.available()); + } +} diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/extension/DefaultClientKexExtensionHandler.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/extension/DefaultClientKexExtensionHandler.java index a0924c559..75d14c4b2 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/kex/extension/DefaultClientKexExtensionHandler.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/extension/DefaultClientKexExtensionHandler.java @@ -32,6 +32,7 @@ import org.apache.sshd.common.AttributeRepository.AttributeKey; import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.kex.extension.parser.GlobalRequestsOk; import org.apache.sshd.common.kex.extension.parser.HostBoundPubkeyAuthentication; import org.apache.sshd.common.kex.extension.parser.ServerSignatureAlgorithms; import org.apache.sshd.common.session.Session; @@ -92,6 +93,8 @@ public boolean handleKexExtensionRequest( } else { session.setAttribute(HOSTBOUND_AUTHENTICATION, version); } + } else if (GlobalRequestsOk.NAME.equals(name)) { + GlobalRequestsOk.INSTANCE.parseExtension(data); } return true; } @@ -157,7 +160,7 @@ public void sendKexExtensions(Session session, KexPhase phase) throws Exception * Collects extension info records, handing them off to the given {@code marshaller} for writing into an * {@link KexExtensions#SSH_MSG_EXT_INFO} message. *

- * This default implementation does not marshal any extension. + * This default implementation marshals a {@link GlobalRequestsOk} extension}. *

* * @param session {@link Session} to send the KEX extension information for @@ -165,5 +168,7 @@ public void sendKexExtensions(Session session, KexPhase phase) throws Exception * @param marshaller {@link BiConsumer} writing the extensions into an SSH message */ public void collectExtensions(Session session, KexPhase phase, BiConsumer marshaller) { + // global-requests-ok + marshaller.accept(GlobalRequestsOk.NAME, ""); } } diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/extension/DefaultServerKexExtensionHandler.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/extension/DefaultServerKexExtensionHandler.java index 7d5924622..07ebb4bd1 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/kex/extension/DefaultServerKexExtensionHandler.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/extension/DefaultServerKexExtensionHandler.java @@ -19,6 +19,7 @@ package org.apache.sshd.common.kex.extension; +import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashMap; @@ -27,6 +28,7 @@ import org.apache.sshd.common.AttributeRepository.AttributeKey; import org.apache.sshd.common.kex.KexProposalOption; +import org.apache.sshd.common.kex.extension.parser.GlobalRequestsOk; import org.apache.sshd.common.kex.extension.parser.ServerSignatureAlgorithms; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.util.GenericUtils; @@ -130,6 +132,16 @@ public void sendKexExtensions(Session session, KexPhase phase) throws Exception } } + @Override + public boolean handleKexExtensionRequest( + Session session, int index, int count, String name, byte[] data) + throws IOException { + if (GlobalRequestsOk.NAME.equals(name)) { + GlobalRequestsOk.INSTANCE.parseExtension(data); + } + return true; + } + /** * Collects extension info records, handing them off to the given {@code marshaller} for writing into an * {@link KexExtensions#SSH_MSG_EXT_INFO} message. @@ -157,5 +169,7 @@ public void collectExtensions(Session session, KexPhase phase, BiConsumer