All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.sshd.common.kex.extension.DefaultServerKexExtensionHandler Maven / Gradle / Ivy

There is a newer version: 2.14.0
Show newest version
/*
 * 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;

import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiConsumer;

import org.apache.sshd.common.AttributeRepository.AttributeKey;
import org.apache.sshd.common.kex.KexProposalOption;
import org.apache.sshd.common.kex.extension.parser.ServerSignatureAlgorithms;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;

/**
 * A basic default implementation of a server-side {@link KexExtensionHandler} handling the
 * {@link ServerSignatureAlgorithms} KEX extension.
 *
 * @see RFC 8308
 */
public class DefaultServerKexExtensionHandler extends AbstractLoggingBean implements KexExtensionHandler {

    /** Default singleton instance. */
    public static final DefaultServerKexExtensionHandler INSTANCE = new DefaultServerKexExtensionHandler();

    /**
     * Session {@link AttributeKey} storing whether the client requested to get the EXT_INFO message. Possible values
     * are:
     * 
*
{@code null}
*
Unknown. We have not yet received the client's KEX proposal. Do not send the message.
*
{@code Boolean.TRUE}
*
We have received the client's KEX proposal, and the client has requested to get the EXT_INFO message.
*
{@code Boolean.FALSE}
*
We have received the client's KEX proposal, and the client did not request to get the EXT_INFO message. Do * not send the message.
*
*/ public static final AttributeKey CLIENT_REQUESTED_EXT_INFO = new AttributeKey<>(); /** * Session {@link AttributeKey} storing whether the server sent an EXT_INFO message at {@link KexPhase#NEWKEYS}. A * server is supposed to send the message at that point only on the very first NEWKEYS message. Possible values are: *
*
{@code null} or {@code Boolean.FALSE}
*
The EXT_INFO message at {@link KexPhase#NEWKEYS} was not done yet.
*
{@code Boolean.TRUE}
*
The EXT_INFO message at {@link KexPhase#NEWKEYS} was done.
*
*/ @SuppressWarnings("javadoc") public static final AttributeKey EXT_INFO_SENT_AT_NEWKEYS = new AttributeKey<>(); public DefaultServerKexExtensionHandler() { super(); } @Override public void handleKexInitProposal(Session session, boolean initiator, Map proposal) throws Exception { if (!initiator) { if (session.getAttribute(CLIENT_REQUESTED_EXT_INFO) == null) { // Only the first time, not on re-KEX String algorithms = proposal.get(KexProposalOption.ALGORITHMS); boolean clientWantsExtInfo = Arrays.asList(GenericUtils.split(algorithms, ',')) .contains(KexExtensions.CLIENT_KEX_EXTENSION); session.setAttribute(CLIENT_REQUESTED_EXT_INFO, clientWantsExtInfo); if (clientWantsExtInfo && log.isTraceEnabled()) { log.trace("handleKexInitProposal({}): got ext-info-c from client", session); } } } } @Override public void sendKexExtensions(Session session, KexPhase phase) throws Exception { if (phase == KexPhase.NEWKEYS) { Boolean alreadySent = session.getAttribute(EXT_INFO_SENT_AT_NEWKEYS); if ((alreadySent != null) && alreadySent.booleanValue()) { // It's not the first NEWKEYS. return; } session.setAttribute(EXT_INFO_SENT_AT_NEWKEYS, Boolean.TRUE); } Boolean doExtInfo = session.getAttribute(CLIENT_REQUESTED_EXT_INFO); if ((doExtInfo == null) || !doExtInfo.booleanValue()) { if (log.isTraceEnabled()) { log.trace("sendKexExtensions({})[{}]: client did not send ext-info-c; skipping sending SSH_MSG_EXT_INFO", session, phase); } return; } Map 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()); } // We must send the SSH_MSG_EXT_INFO as the next packet following our SSH_MSG_NEWKEYS message, which we just // sent. RFC 8308 recommends that "the server sends its SSH_MSG_EXT_INFO not only as the next packet after // SSH_MSG_NEWKEYS, but without delay". Note that the message will never be queued since it has low command // ID; SSH_MSG_EXT_INFO is 7. session.writePacket(buffer); } else if (log.isDebugEnabled()) { log.debug("sendKexExtensions({})[{}]: no extension info; skipping sending SSH_MSG_EXT_INFO", session, phase); } } /** * 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 marshals a {@link ServerSignatureAlgorithms} extension if the {@code phase} is * {@link KexPhase#NEWKEYS}. *

* * @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 */ @SuppressWarnings("javadoc") public void collectExtensions(Session session, KexPhase phase, BiConsumer marshaller) { if (phase == KexPhase.NEWKEYS) { Collection algorithms = session.getSignatureFactoriesNames(); if (!GenericUtils.isEmpty(algorithms)) { marshaller.accept(ServerSignatureAlgorithms.NAME, algorithms); if (log.isDebugEnabled()) { log.debug("collectExtensions({})[{}]: extension info {}: {}", session, phase, ServerSignatureAlgorithms.NAME, String.join(",", algorithms)); } } else if (log.isWarnEnabled()) { log.warn("collectExtensions({})[{}]: extension info {} has no algorithms; skipping", session, phase, ServerSignatureAlgorithms.NAME); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy