blob: 745921a7f4412197cdf41b872b95ae6109270380 [file] [log] [blame]
/*
* 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.io.IOException;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import org.apache.sshd.common.kex.KexProposalOption;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.util.buffer.Buffer;
/**
* Used to support <A HREF="https://tools.ietf.org/html/rfc8308">RFC 8308</A>
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public interface KexExtensionHandler {
/**
* Provides a hint as to the context in which {@code isKexExtensionsAvailable} is invoked
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
enum AvailabilityPhase {
/**
* Decide whether to delay sending the KEX-INIT message until the peer one has been received. <B>Note:</B>
* currently invoked only by client sessions, but code should not rely on this implicit assumption.
*/
PREKEX,
/**
* About to create the KEX-INIT proposal - should this session declare it support the KEX negotiation extension
* mechanism or not.
*/
PROPOSAL,
/**
* About to send the {@code SSH_MSG_NEWKEYS} message
*/
NEWKEYS,
/**
* About to send {@code SSH_MSG_USERAUTH_SUCCESS} message. <B>Note:</B> currently invoked only by server
* sessions, but code should not rely on this implicit assumption.
*/
AUTHOK;
}
/**
* @param session The {@link Session} about to execute KEX
* @param phase The {@link AvailabilityPhase} hint as to why the query is being made
* @return {@code true} whether to KEX extensions are supported/allowed for the session
* @throws IOException If failed to process the request
*/
default boolean isKexExtensionsAvailable(Session session, AvailabilityPhase phase) throws IOException {
return true;
}
/**
* Invoked when a peer is ready to send the KEX options proposal or has received such a proposal. <B>Note:</B> this
* method is called during the negotiation phase even if {@code isKexExtensionsAvailable} returns {@code false} for
* the session.
*
* @param session The {@link Session} initiating or receiving the proposal
* @param initiator {@code true} if the proposal is about to be sent, {@code false} if this is a proposal received
* from the peer.
* @param proposal The proposal contents - <B>Caveat emptor:</B> the proposal is <U>modifiable</U> i.e., the
* handler can modify it before being sent or before being processed (if incoming)
* @throws Exception If failed to handle the request
*/
default void handleKexInitProposal(
Session session, boolean initiator, Map<KexProposalOption, String> proposal)
throws Exception {
// ignored
}
/**
* Invoked during the KEX negotiation phase to inform about option being negotiated. <B>Note:</B> this method is
* called during the negotiation phase even if {@code isKexExtensionsAvailable} returns {@code false} for the
* session.
*
* @param session The {@link Session} executing the negotiation
* @param option The negotiated {@link KexProposalOption}
* @param nValue The negotiated option value (may be {@code null}/empty).
* @param c2sOptions The client proposals
* @param cValue The client-side value for the option (may be {@code null}/empty).
* @param s2cOptions The server proposals
* @param sValue The server-side value for the option (may be {@code null}/empty).
* @throws Exception If failed to handle the invocation
*/
default void handleKexExtensionNegotiation(
Session session, KexProposalOption option, String nValue,
Map<KexProposalOption, String> c2sOptions, String cValue,
Map<KexProposalOption, String> s2cOptions, String sValue)
throws Exception {
// do nothing
}
/**
* The phase at which {@code sendKexExtensions} is invoked
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
enum KexPhase {
NEWKEYS,
AUTHOK;
public static final Set<KexPhase> VALUES = Collections.unmodifiableSet(EnumSet.allOf(KexPhase.class));
}
/**
* Invoked in order to allow the handler to send an {@code SSH_MSG_EXT_INFO} message. <B>Note:</B> this method is
* called only if {@code isKexExtensionsAvailable} returns {@code true} for the session.
*
* @param session The {@link Session}
* @param phase The phase at which the handler is invoked
* @throws Exception If failed to handle the invocation
* @see <A HREF="https://tools.ietf.org/html/rfc8308#section-2.4">RFC-8308 - section 2.4</A>
*/
default void sendKexExtensions(Session session, KexPhase phase) throws Exception {
// do nothing
}
/**
* Parses the {@code SSH_MSG_EXT_INFO} message. <B>Note:</B> this method is called regardless of whether
* {@code isKexExtensionsAvailable} returns {@code true} for the session.
*
* @param session The {@link Session} through which the message was received
* @param buffer The message buffer
* @return {@code true} if message handled - if {@code false} then {@code SSH_MSG_UNIMPLEMENTED} will be
* generated
* @throws Exception If failed to handle the message
* @see <A HREF="https://tools.ietf.org/html/rfc8308#section-2.3">RFC-8308 - section 2.3</A>
* @see #handleKexExtensionRequest(Session, int, int, String, byte[])
*/
default boolean handleKexExtensionsMessage(Session session, Buffer buffer) throws Exception {
int count = buffer.getInt();
for (int index = 0; index < count; index++) {
String name = buffer.getString();
byte[] data = buffer.getBytes();
if (!handleKexExtensionRequest(session, index, count, name, data)) {
break;
}
}
return true;
}
/**
* Parses the {@code SSH_MSG_NEWCOMPRESS} message. <B>Note:</B> this method is called regardless of whether
* {@code isKexExtensionsAvailable} returns {@code true} for the session.
*
* @param session The {@link Session} through which the message was received
* @param buffer The message buffer
* @return {@code true} if message handled - if {@code false} then {@code SSH_MSG_UNIMPLEMENTED} will be
* generated
* @throws Exception If failed to handle the message
* @see <A HREF="https://tools.ietf.org/html/rfc8308#section-3.2">RFC-8308 - section 3.2</A>
*/
default boolean handleKexCompressionMessage(Session session, Buffer buffer) throws Exception {
return true;
}
/**
* Invoked by {@link #handleKexExtensionsMessage(Session, Buffer)} in order to handle a specific extension.
*
* @param session The {@link Session} through which the message was received
* @param index The 0-based extension index
* @param count The total extensions in the message
* @param name The extension name
* @param data The extension data
* @return {@code true} whether to proceed to the next extension or stop processing the rest
* @throws Exception If failed to handle the extension
*/
default boolean handleKexExtensionRequest(
Session session, int index, int count, String name, byte[] data)
throws Exception {
return true;
}
}