mirror of https://github.com/hashicorp/boundary
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
306 lines
8.2 KiB
306 lines
8.2 KiB
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package ssh
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/boundary/internal/bsr"
|
|
pssh "github.com/hashicorp/boundary/internal/bsr/gen/ssh/v1"
|
|
"github.com/hashicorp/boundary/internal/bsr/internal/is"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
func init() {
|
|
for _, ct := range []bsr.ChunkType{
|
|
DataChunkType,
|
|
BreakReqChunkType,
|
|
CancelTCPIPForwardReqChunkType,
|
|
DirectTCPIPReqChunkType,
|
|
EnvReqChunkType,
|
|
ExecReqChunkType,
|
|
ExitSignalReqChunkType,
|
|
ExitStatusReqChunkType,
|
|
ForwardedTCPIPReqChunkType,
|
|
PtyReqChunkType,
|
|
SessionReqChunkType,
|
|
ShellReqChunkType,
|
|
SignalReqChunkType,
|
|
SubsystemReqChunkType,
|
|
TCPIPForwardReqChunkType,
|
|
WindowChangeReqChunkType,
|
|
X11ForwardingReqChunkType,
|
|
X11ReqChunkType,
|
|
XonXoffReqChunkType,
|
|
UnknownReqChunkType,
|
|
} {
|
|
if err := bsr.RegisterChunkType(Protocol, ct, DecodeChunk); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
const (
|
|
// Protocol is used to identify chunks that are recorded from SSH.
|
|
Protocol bsr.Protocol = "BSSH"
|
|
|
|
// MaxPacketSize is used by the DataWriter to determine if SSH data should
|
|
// be broken into multiple chunks.
|
|
MaxPacketSize = 256 * 1024
|
|
)
|
|
|
|
// Chunk types
|
|
const (
|
|
DataChunkType bsr.ChunkType = "DATA"
|
|
BreakReqChunkType bsr.ChunkType = "BREK"
|
|
CancelTCPIPForwardReqChunkType bsr.ChunkType = "CTPF"
|
|
DirectTCPIPReqChunkType bsr.ChunkType = "DTCP"
|
|
EnvReqChunkType bsr.ChunkType = "ENVR"
|
|
ExecReqChunkType bsr.ChunkType = "EXEC"
|
|
ExitSignalReqChunkType bsr.ChunkType = "EXSG"
|
|
ExitStatusReqChunkType bsr.ChunkType = "EXST"
|
|
ForwardedTCPIPReqChunkType bsr.ChunkType = "FTCP"
|
|
PtyReqChunkType bsr.ChunkType = "PTYR"
|
|
SessionReqChunkType bsr.ChunkType = "SESS"
|
|
ShellReqChunkType bsr.ChunkType = "SHLL"
|
|
SignalReqChunkType bsr.ChunkType = "SGNL"
|
|
SubsystemReqChunkType bsr.ChunkType = "SUBS"
|
|
TCPIPForwardReqChunkType bsr.ChunkType = "TCPF"
|
|
UnknownReqChunkType bsr.ChunkType = "UNKR"
|
|
WindowChangeReqChunkType bsr.ChunkType = "WCHG"
|
|
X11ForwardingReqChunkType bsr.ChunkType = "X11F"
|
|
X11ReqChunkType bsr.ChunkType = "X11R"
|
|
XonXoffReqChunkType bsr.ChunkType = "XOXO"
|
|
)
|
|
|
|
// DataChunk contains the raw byte data from an SSH session
|
|
type DataChunk struct {
|
|
*bsr.BaseChunk
|
|
Data []byte
|
|
}
|
|
|
|
// NewDataChunk constructs a DataChunk
|
|
func NewDataChunk(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, data []byte) (*DataChunk, error) {
|
|
const op = "ssh.NewDataChunk"
|
|
|
|
baseChunk, err := bsr.NewBaseChunk(ctx, Protocol, d, t, DataChunkType)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: unable to create base chunk: %w", op, err)
|
|
}
|
|
|
|
return &DataChunk{
|
|
BaseChunk: baseChunk,
|
|
Data: data,
|
|
}, nil
|
|
}
|
|
|
|
// MarshalData returns the data for a DataChunk
|
|
func (c *DataChunk) MarshalData(_ context.Context) ([]byte, error) {
|
|
return c.Data, nil
|
|
}
|
|
|
|
// DecodeChunk will decode any known SSH Chunk type. If the chunk type is
|
|
// not an ssh chunk type, and error is returned.
|
|
func DecodeChunk(_ context.Context, bc *bsr.BaseChunk, data []byte) (bsr.Chunk, error) {
|
|
const op = "ssh.DecodeChunk"
|
|
|
|
if is.Nil(bc) {
|
|
return nil, fmt.Errorf("%s: nil base chunk: %w", op, bsr.ErrInvalidParameter)
|
|
}
|
|
|
|
if bc.Protocol != Protocol {
|
|
return nil, fmt.Errorf("%s: invalid protocol %s", op, bc.Protocol)
|
|
}
|
|
|
|
switch bc.Type {
|
|
case DataChunkType:
|
|
return &DataChunk{
|
|
BaseChunk: bc,
|
|
Data: data,
|
|
}, nil
|
|
}
|
|
|
|
if len(data) <= 0 {
|
|
return nil, fmt.Errorf("%s: not enough data", op)
|
|
}
|
|
|
|
switch bc.Type {
|
|
case BreakReqChunkType:
|
|
mm := &pssh.BreakRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &BreakRequest{
|
|
BaseChunk: bc,
|
|
BreakRequest: mm,
|
|
}, nil
|
|
case CancelTCPIPForwardReqChunkType:
|
|
mm := &pssh.CancelTCPIPForwardRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &CancelTCPIPForwardRequest{
|
|
BaseChunk: bc,
|
|
CancelTCPIPForwardRequest: mm,
|
|
}, nil
|
|
case DirectTCPIPReqChunkType:
|
|
mm := &pssh.DirectTCPIPRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &DirectTCPIPRequest{
|
|
BaseChunk: bc,
|
|
DirectTCPIPRequest: mm,
|
|
}, nil
|
|
case EnvReqChunkType:
|
|
mm := &pssh.EnvRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &EnvRequest{
|
|
BaseChunk: bc,
|
|
EnvRequest: mm,
|
|
}, nil
|
|
case ExecReqChunkType:
|
|
mm := &pssh.ExecRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &ExecRequest{
|
|
BaseChunk: bc,
|
|
ExecRequest: mm,
|
|
}, nil
|
|
case ExitSignalReqChunkType:
|
|
mm := &pssh.ExitSignalRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &ExitSignalRequest{
|
|
BaseChunk: bc,
|
|
ExitSignalRequest: mm,
|
|
}, nil
|
|
case ExitStatusReqChunkType:
|
|
mm := &pssh.ExitStatusRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &ExitStatusRequest{
|
|
BaseChunk: bc,
|
|
ExitStatusRequest: mm,
|
|
}, nil
|
|
case ForwardedTCPIPReqChunkType:
|
|
mm := &pssh.ForwardedTCPIPRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &ForwardedTCPIPRequest{
|
|
BaseChunk: bc,
|
|
ForwardedTCPIPRequest: mm,
|
|
}, nil
|
|
case PtyReqChunkType:
|
|
mm := &pssh.PtyRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &PtyRequest{
|
|
BaseChunk: bc,
|
|
PtyRequest: mm,
|
|
}, nil
|
|
case SessionReqChunkType:
|
|
mm := &pssh.SessionRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &SessionRequest{
|
|
BaseChunk: bc,
|
|
SessionRequest: mm,
|
|
}, nil
|
|
case ShellReqChunkType:
|
|
mm := &pssh.ShellRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &ShellRequest{
|
|
BaseChunk: bc,
|
|
ShellRequest: mm,
|
|
}, nil
|
|
case SignalReqChunkType:
|
|
mm := &pssh.SignalRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &SignalRequest{
|
|
BaseChunk: bc,
|
|
SignalRequest: mm,
|
|
}, nil
|
|
case SubsystemReqChunkType:
|
|
mm := &pssh.SubsystemRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &SubsystemRequest{
|
|
BaseChunk: bc,
|
|
SubsystemRequest: mm,
|
|
}, nil
|
|
case TCPIPForwardReqChunkType:
|
|
mm := &pssh.TCPIPForwardRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &TCPIPForwardRequest{
|
|
BaseChunk: bc,
|
|
TCPIPForwardRequest: mm,
|
|
}, nil
|
|
case WindowChangeReqChunkType:
|
|
mm := &pssh.WindowChangeRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &WindowChangeRequest{
|
|
BaseChunk: bc,
|
|
WindowChangeRequest: mm,
|
|
}, nil
|
|
case X11ForwardingReqChunkType:
|
|
mm := &pssh.X11ForwardingRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &X11ForwardingRequest{
|
|
BaseChunk: bc,
|
|
X11ForwardingRequest: mm,
|
|
}, nil
|
|
case X11ReqChunkType:
|
|
mm := &pssh.X11Request{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &X11Request{
|
|
BaseChunk: bc,
|
|
X11Request: mm,
|
|
}, nil
|
|
case XonXoffReqChunkType:
|
|
mm := &pssh.XonXoffRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &XonXoffRequest{
|
|
BaseChunk: bc,
|
|
XonXoffRequest: mm,
|
|
}, nil
|
|
case UnknownReqChunkType:
|
|
mm := &pssh.UnknownRequest{}
|
|
if err := proto.Unmarshal(data, mm); err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return &UnknownRequest{
|
|
BaseChunk: bc,
|
|
UnknownRequest: mm,
|
|
}, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("%s: unsupported chunk type %s", op, bc.Type)
|
|
}
|
|
}
|