You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2019/09/23 04:51:20 UTC

[mina-sshd] branch master updated: [SSHD-942] Added ChannelIdTrackingUnknownChannelReferenceHandler

This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git


The following commit(s) were added to refs/heads/master by this push:
     new 11b33de  [SSHD-942] Added ChannelIdTrackingUnknownChannelReferenceHandler
11b33de is described below

commit 11b33dee37b5b9c71a40a8a98a42007e3687131e
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Mon Sep 23 07:44:28 2019 +0300

    [SSHD-942] Added ChannelIdTrackingUnknownChannelReferenceHandler
---
 CHANGES.md                                         |  4 ++
 docs/event-listeners.md                            |  4 ++
 ...elIdTrackingUnknownChannelReferenceHandler.java | 83 ++++++++++++++++++++++
 .../DefaultUnknownChannelReferenceHandler.java     | 16 +++--
 4 files changed, 101 insertions(+), 6 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 7f77a16..1de1064 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -36,6 +36,10 @@ the standard does not specifically specify the behavior regarding symbolic links
 * `SessionListener` supports `sessionPeerIdentificationReceived` that is invoked once successful
 peer version data is received.
 
+* `ChannelIdTrackingUnknownChannelReferenceHandler` extends the functionality of the `DefaultUnknownChannelReferenceHandler`
+by tracking the initialized channels identifiers and being lenient only if command is received for a channel that was
+initialized in the past.
+
 ## Behavioral changes and enhancements
 
 * [SSHD-926](https://issues.apache.org/jira/browse/SSHD-930) - Add support for OpenSSH 'lsetstat@openssh.com' SFTP protocol extension.
diff --git a/docs/event-listeners.md b/docs/event-listeners.md
index 476ec13..49a4a83 100644
--- a/docs/event-listeners.md
+++ b/docs/event-listeners.md
@@ -80,6 +80,10 @@ and logs them at DEBUG level. For a select few types of messages the code genera
 peer session - see `DefaultUnknownChannelReferenceHandler` implementation. The user may register handlers at any level - client/server, session
 and/or connection service - the one registered "closest" to connection service will be used.
 
+An **experimental** `ChannelIdTrackingUnknownChannelReferenceHandler` is available in _sshd-contrib_ package that applies the "leniency" of
+the `DefaultUnknownChannelReferenceHandler` only if the unknown channel is one that has been assigned in the past - otherwise it throws an
+exception. In order to use it, the handler instance needs to be registered as **both** an `UnknownChannelReferenceHandler` and a `ChannelListener`.
+
 ### `KexExtensionHandler`
 
 Provides hooks for implementing [KEX extension negotiation](https://tools.ietf.org/html/rfc8308).
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/session/helpers/ChannelIdTrackingUnknownChannelReferenceHandler.java b/sshd-contrib/src/main/java/org/apache/sshd/common/session/helpers/ChannelIdTrackingUnknownChannelReferenceHandler.java
new file mode 100644
index 0000000..bb6498b
--- /dev/null
+++ b/sshd-contrib/src/main/java/org/apache/sshd/common/session/helpers/ChannelIdTrackingUnknownChannelReferenceHandler.java
@@ -0,0 +1,83 @@
+/*
+ * 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.session.helpers;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.AttributeRepository.AttributeKey;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.channel.ChannelListener;
+import org.apache.sshd.common.channel.exception.SshChannelNotFoundException;
+import org.apache.sshd.common.session.ConnectionService;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * Makes sure that the referenced &quot;unknown&quot; channel identifier
+ * is one that was assigned in the past. <B>Note:</B> it relies on the
+ * fact that the default {@code ConnectionService} implementation assigns
+ * channels identifiers in ascending order.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ChannelIdTrackingUnknownChannelReferenceHandler
+        extends DefaultUnknownChannelReferenceHandler
+        implements ChannelListener {
+    public static final AttributeKey<Integer> LAST_CHANNEL_ID_KEY = new AttributeKey<>();
+
+    public static final ChannelIdTrackingUnknownChannelReferenceHandler TRACKER =
+        new ChannelIdTrackingUnknownChannelReferenceHandler();
+
+    public ChannelIdTrackingUnknownChannelReferenceHandler() {
+        super();
+    }
+
+    @Override
+    public void channelInitialized(Channel channel) {
+        int channelId = channel.getId();
+        Session session = channel.getSession();
+        Integer lastTracked = session.setAttribute(LAST_CHANNEL_ID_KEY, channelId);
+        if (log.isDebugEnabled()) {
+            log.debug("channelInitialized({}) updated last tracked channel ID {} => {}",
+                channel, lastTracked, channelId);
+        }
+    }
+
+    @Override
+    public Channel handleUnknownChannelCommand(
+            ConnectionService service, byte cmd, int channelId, Buffer buffer)
+                throws IOException {
+        Session session = service.getSession();
+        Integer lastTracked = session.getAttribute(LAST_CHANNEL_ID_KEY);
+        if ((lastTracked != null) && (channelId <= lastTracked.intValue())) {
+            // Use TRACE level in order to avoid messages flooding
+            if (log.isTraceEnabled()) {
+                log.trace("handleUnknownChannelCommand({}) apply default handling for {} on channel={} (lastTracked={})",
+                    session, SshConstants.getCommandMessageName(cmd), channelId, lastTracked);
+            }
+            return super.handleUnknownChannelCommand(service, cmd, channelId, buffer);
+        }
+
+        throw new SshChannelNotFoundException(channelId,
+            "Received " + SshConstants.getCommandMessageName(cmd) + " on unassigned channel " + channelId
+            + " (last assigned=" + lastTracked + ")");
+    }
+}
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/DefaultUnknownChannelReferenceHandler.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/DefaultUnknownChannelReferenceHandler.java
index 76ebec2..8438ca7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/DefaultUnknownChannelReferenceHandler.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/DefaultUnknownChannelReferenceHandler.java
@@ -55,13 +55,13 @@ public class DefaultUnknownChannelReferenceHandler
     @Override
     public Channel handleUnknownChannelCommand(
             ConnectionService service, byte cmd, int channelId, Buffer buffer)
-                    throws IOException {
+                throws IOException {
         Session session = service.getSession();
         // Use DEBUG level to avoid log overflow due to invalid messages flood
         boolean debugEnabled = log.isDebugEnabled();
         if (debugEnabled) {
             log.debug("handleUnknownChannelCommand({}) received {} command for unknown channel: {}",
-                    session, SshConstants.getCommandMessageName(cmd), channelId);
+                session, SshConstants.getCommandMessageName(cmd), channelId);
         }
 
         boolean wantReply = false;
@@ -85,10 +85,12 @@ public class DefaultUnknownChannelReferenceHandler
 
             case SshConstants.SSH_MSG_CHANNEL_DATA:
             case SshConstants.SSH_MSG_CHANNEL_EXTENDED_DATA:
-                wantReply = PropertyResolverUtils.getBooleanProperty(session, SEND_REPLY_FOR_CHANNEL_DATA, DEFAULT_SEND_REPLY_FOR_CHANNEL_DATA);
+                wantReply = PropertyResolverUtils.getBooleanProperty(
+                    session, SEND_REPLY_FOR_CHANNEL_DATA, DEFAULT_SEND_REPLY_FOR_CHANNEL_DATA);
                 // Use TRACE level to avoid log overflow due to invalid messages flood
                 if (log.isTraceEnabled()) {
-                    log.trace("handleUnknownChannelCommand({}) received msg channel data (opcode={}) reply={}", session, cmd, wantReply);
+                    log.trace("handleUnknownChannelCommand({}) received msg channel data (opcode={}) reply={}",
+                        session, cmd, wantReply);
                 }
                 break;
 
@@ -102,12 +104,14 @@ public class DefaultUnknownChannelReferenceHandler
         return null;
     }
 
-    protected IoWriteFuture sendFailureResponse(ConnectionService service, byte cmd, int channelId) throws IOException {
+    protected IoWriteFuture sendFailureResponse(
+            ConnectionService service, byte cmd, int channelId)
+                throws IOException {
         Session session = service.getSession();
         // Use DEBUG level to avoid log overflow due to invalid messages flood
         if (log.isDebugEnabled()) {
             log.debug("sendFailureResponse({}) send SSH_MSG_CHANNEL_FAILURE for {} command on unknown channel: {}",
-                    session, SshConstants.getCommandMessageName(cmd), channelId);
+                session, SshConstants.getCommandMessageName(cmd), channelId);
         }
 
         Buffer rsp = session.createBuffer(SshConstants.SSH_MSG_CHANNEL_FAILURE, Integer.BYTES);