feat(bsr/ssh): Support decoding chunks

This adds a function for decoding all of the SSH Chunk types and
registers the function with the chunk types so that the bsr.ChunkDecode
can be used to decode SSH Chunks.
This commit is contained in:
Timothy Messier committed 2023-05-31 12:18:20 +00:00
1 parent fe870fc989
commit f983a05a3e
21 files changed
+1023 -41

No files matched your search

+243 -3
View File
@@ -8,14 +8,51 @@ import (
"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 = "SSH\x87"
// MaxPacketSize is used by the DataWriter to determine if SSH data should
// be broken into multiple chunks.
MaxPacketSize = 256 * 1024
)
// Chunk types
// Chunk types
const (
DataChunkType bsr.ChunkType = "DATA"
BreakReqChunkType bsr.ChunkType = "BREK"
CancelTCPIPForwardReqChunkType bsr.ChunkType = "CTPF"
@@ -40,7 +77,7 @@ const (
// DataChunk contains the raw byte data from an SSH session
type DataChunk struct {
bsr.BaseChunk
*bsr.BaseChunk
Data []byte
}
@@ -54,7 +91,7 @@ func NewDataChunk(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, data [
}
return &DataChunk{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
Data: data,
}, nil
}
@@ -63,3 +100,206 @@ func NewDataChunk(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, data [
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)
}
}
+2 -2
View File
@@ -18,7 +18,7 @@ const BreakRequestType = "break"
// BreakRequest is a chunk to contain data for an SSH Break request
type BreakRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.BreakRequest
}
@@ -63,7 +63,7 @@ func NewBreakRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, r *
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &BreakRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
BreakRequest: &pssh.BreakRequest{
RequestType: r.Type,
WantReply: r.WantReply,
@@ -18,7 +18,7 @@ const CancelTCPIPForwardRequestType = "cancel-tcpip-forward"
// CancelTCPIPForwardRequest is a chunk to contain data for an SSH Cancel TCIPIP Forward request
type CancelTCPIPForwardRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.CancelTCPIPForwardRequest
}
@@ -63,7 +63,7 @@ func NewCancelTCPIPForwardRequest(ctx context.Context, d bsr.Direction, t *bsr.T
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &CancelTCPIPForwardRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
CancelTCPIPForwardRequest: &pssh.CancelTCPIPForwardRequest{
RequestType: r.Type,
WantReply: r.WantReply,
+742
View File
@@ -0,0 +1,742 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package ssh_test
import (
"context"
"errors"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/boundary/internal/bsr"
pssh "github.com/hashicorp/boundary/internal/bsr/gen/ssh/v1"
"github.com/hashicorp/boundary/internal/bsr/ssh"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
gossh "golang.org/x/crypto/ssh"
"google.golang.org/protobuf/proto"
)
func TestDecodeChunk(t *testing.T) {
ctx := context.Background()
cases := []struct {
bc *bsr.BaseChunk
encoded []byte
want bsr.Chunk
}{
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.DataChunkType,
},
[]byte("foo"),
&ssh.DataChunk{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.DataChunkType,
},
Data: []byte("foo"),
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.BreakReqChunkType,
},
func() []byte {
msg := &pssh.BreakRequest{
RequestType: ssh.BreakRequestType,
WantReply: false,
BreakLengthMs: 43,
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.BreakRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.BreakReqChunkType,
},
BreakRequest: &pssh.BreakRequest{
RequestType: ssh.BreakRequestType,
WantReply: false,
BreakLengthMs: 43,
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.CancelTCPIPForwardReqChunkType,
},
func() []byte {
msg := &pssh.CancelTCPIPForwardRequest{
RequestType: ssh.CancelTCPIPForwardRequestType,
WantReply: false,
AddressToBind: "127.0.0.1",
PortToBind: 87565,
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.CancelTCPIPForwardRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.CancelTCPIPForwardReqChunkType,
},
CancelTCPIPForwardRequest: &pssh.CancelTCPIPForwardRequest{
RequestType: ssh.CancelTCPIPForwardRequestType,
WantReply: false,
AddressToBind: "127.0.0.1",
PortToBind: 87565,
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.DirectTCPIPReqChunkType,
},
func() []byte {
msg := &pssh.DirectTCPIPRequest{
RequestType: ssh.DirectTCPIPRequestType,
SenderChannel: 5,
InitialWindowSize: 60,
MaximumPacketSize: 512,
Host: "127.0.0.1",
Port: 87654,
OriginatorIpAddress: "10.0.0.1",
OriginatorPort: 22,
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.DirectTCPIPRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.DirectTCPIPReqChunkType,
},
DirectTCPIPRequest: &pssh.DirectTCPIPRequest{
RequestType: ssh.DirectTCPIPRequestType,
SenderChannel: 5,
InitialWindowSize: 60,
MaximumPacketSize: 512,
Host: "127.0.0.1",
Port: 87654,
OriginatorIpAddress: "10.0.0.1",
OriginatorPort: 22,
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.EnvReqChunkType,
},
func() []byte {
msg := &pssh.EnvRequest{
RequestType: ssh.EnvRequestType,
WantReply: false,
VariableName: "FOO",
VariableValue: "bar",
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.EnvRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.EnvReqChunkType,
},
EnvRequest: &pssh.EnvRequest{
RequestType: ssh.EnvRequestType,
WantReply: false,
VariableName: "FOO",
VariableValue: "bar",
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.ExecReqChunkType,
},
func() []byte {
msg := &pssh.ExecRequest{
RequestType: ssh.ExecRequestType,
WantReply: false,
Command: "/bin/run-all-the-things.sh",
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.ExecRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.ExecReqChunkType,
},
ExecRequest: &pssh.ExecRequest{
RequestType: ssh.ExecRequestType,
WantReply: false,
Command: "/bin/run-all-the-things.sh",
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.ExitSignalReqChunkType,
},
func() []byte {
msg := &pssh.ExitSignalRequest{
RequestType: ssh.ExitSignalRequestType,
WantReply: false,
SignalName: "HUP",
CoreDumped: false,
ErrorMessage: "Failed Release",
LanguageTag: "en-US",
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.ExitSignalRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.ExitSignalReqChunkType,
},
ExitSignalRequest: &pssh.ExitSignalRequest{
RequestType: ssh.ExitSignalRequestType,
WantReply: false,
SignalName: "HUP",
CoreDumped: false,
ErrorMessage: "Failed Release",
LanguageTag: "en-US",
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.ExitStatusReqChunkType,
},
func() []byte {
msg := &pssh.ExitStatusRequest{
RequestType: ssh.ExitStatusRequestType,
WantReply: false,
ExitStatus: 34,
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.ExitStatusRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.ExitStatusReqChunkType,
},
ExitStatusRequest: &pssh.ExitStatusRequest{
RequestType: ssh.ExitStatusRequestType,
WantReply: false,
ExitStatus: 34,
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.ForwardedTCPIPReqChunkType,
},
func() []byte {
msg := &pssh.ForwardedTCPIPRequest{
RequestType: ssh.ForwardedTCPIPRequestType,
SenderChannel: 5,
InitialWindowSize: 70,
MaximumPacketSize: 1024,
Address: "10.0.0.45",
Port: 2222,
OriginatorIpAddress: "127.0.0.1",
OriginatorPort: 8765,
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.ForwardedTCPIPRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.ForwardedTCPIPReqChunkType,
},
ForwardedTCPIPRequest: &pssh.ForwardedTCPIPRequest{
RequestType: ssh.ForwardedTCPIPRequestType,
SenderChannel: 5,
InitialWindowSize: 70,
MaximumPacketSize: 1024,
Address: "10.0.0.45",
Port: 2222,
OriginatorIpAddress: "127.0.0.1",
OriginatorPort: 8765,
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.PtyReqChunkType,
},
func() []byte {
msg := &pssh.PtyRequest{
RequestType: ssh.PtyRequestType,
WantReply: false,
TermEnvVar: "xterm",
TerminalWidthCharacters: 80,
TerminalHeightRows: 60,
TerminalWidthPixels: 960,
TerminalHeightPixels: 1080,
EncodedTerminalMode: func() []byte {
var tm []byte
modes := []struct {
Key byte
Val uint32
}{
{gossh.ECHO, 0}, // disable echoing
{gossh.TTY_OP_ISPEED, 14400}, // input speed = 14.4kbaud
{gossh.TTY_OP_OSPEED, 14400}, // output speed = 14.4kbaud
}
for _, m := range modes {
tm = append(tm, gossh.Marshal(&m)...)
}
return tm
}(),
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.PtyRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.PtyReqChunkType,
},
PtyRequest: &pssh.PtyRequest{
RequestType: ssh.PtyRequestType,
WantReply: false,
TermEnvVar: "xterm",
TerminalWidthCharacters: 80,
TerminalHeightRows: 60,
TerminalWidthPixels: 960,
TerminalHeightPixels: 1080,
EncodedTerminalMode: func() []byte {
var tm []byte
modes := []struct {
Key byte
Val uint32
}{
{gossh.ECHO, 0}, // disable echoing
{gossh.TTY_OP_ISPEED, 14400}, // input speed = 14.4kbaud
{gossh.TTY_OP_OSPEED, 14400}, // output speed = 14.4kbaud
}
for _, m := range modes {
tm = append(tm, gossh.Marshal(&m)...)
}
return tm
}(),
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.SessionReqChunkType,
},
func() []byte {
msg := &pssh.SessionRequest{
RequestType: ssh.SessionRequestType,
SenderChannel: 5,
InitialWindowSize: 60,
MaximumPacketSize: 512,
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.SessionRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.SessionReqChunkType,
},
SessionRequest: &pssh.SessionRequest{
RequestType: ssh.SessionRequestType,
SenderChannel: 5,
InitialWindowSize: 60,
MaximumPacketSize: 512,
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.ShellReqChunkType,
},
func() []byte {
msg := &pssh.ShellRequest{
RequestType: ssh.ShellRequestType,
WantReply: false,
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.ShellRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.ShellReqChunkType,
},
ShellRequest: &pssh.ShellRequest{
RequestType: ssh.ShellRequestType,
WantReply: false,
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.SignalReqChunkType,
},
func() []byte {
msg := &pssh.SignalRequest{
RequestType: ssh.SignalRequestType,
WantReply: false,
SignalName: "USR1",
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.SignalRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.SignalReqChunkType,
},
SignalRequest: &pssh.SignalRequest{
RequestType: ssh.SignalRequestType,
WantReply: false,
SignalName: "USR1",
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.SubsystemReqChunkType,
},
func() []byte {
msg := &pssh.SubsystemRequest{
RequestType: ssh.SubsystemRequestType,
WantReply: false,
SubsystemName: "sftp",
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.SubsystemRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.SubsystemReqChunkType,
},
SubsystemRequest: &pssh.SubsystemRequest{
RequestType: ssh.SubsystemRequestType,
WantReply: false,
SubsystemName: "sftp",
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.TCPIPForwardReqChunkType,
},
func() []byte {
msg := &pssh.TCPIPForwardRequest{
RequestType: ssh.TCPIPForwardRequestType,
WantReply: false,
AddressToBind: "127.0.0.1",
PortToBind: 2222,
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.TCPIPForwardRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.TCPIPForwardReqChunkType,
},
TCPIPForwardRequest: &pssh.TCPIPForwardRequest{
RequestType: ssh.TCPIPForwardRequestType,
WantReply: false,
AddressToBind: "127.0.0.1",
PortToBind: 2222,
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.WindowChangeReqChunkType,
},
func() []byte {
msg := &pssh.WindowChangeRequest{
RequestType: ssh.WindowChangeRequestType,
WantReply: false,
TerminalWidthColumns: 120,
TerminalHeightRows: 120,
TerminalWidthPixels: 1920,
TerminalHeightPixels: 1080,
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.WindowChangeRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.WindowChangeReqChunkType,
},
WindowChangeRequest: &pssh.WindowChangeRequest{
RequestType: ssh.WindowChangeRequestType,
WantReply: false,
TerminalWidthColumns: 120,
TerminalHeightRows: 120,
TerminalWidthPixels: 1920,
TerminalHeightPixels: 1080,
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.X11ForwardingReqChunkType,
},
func() []byte {
msg := &pssh.X11ForwardingRequest{
RequestType: ssh.X11ForwardingRequestType,
WantReply: false,
SingleConnection: true,
X11AuthenticationProtocol: "MIT-MAGIC-COOKIE-1",
X11AuthenticationCookie: "6D6167696320636F6F6B6965",
X11ScreenNumber: 10,
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.X11ForwardingRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.X11ForwardingReqChunkType,
},
X11ForwardingRequest: &pssh.X11ForwardingRequest{
RequestType: ssh.X11ForwardingRequestType,
WantReply: false,
SingleConnection: true,
X11AuthenticationProtocol: "MIT-MAGIC-COOKIE-1",
X11AuthenticationCookie: "6D6167696320636F6F6B6965",
X11ScreenNumber: 10,
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.X11ReqChunkType,
},
func() []byte {
msg := &pssh.X11Request{
RequestType: ssh.X11RequestType,
SenderChannel: 6,
InitialWindowSize: 40,
MaximumPacketSize: 512,
OriginatorAddress: "127.0.0.1",
OriginatorPort: 2222,
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.X11Request{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.X11ReqChunkType,
},
X11Request: &pssh.X11Request{
RequestType: ssh.X11RequestType,
SenderChannel: 6,
InitialWindowSize: 40,
MaximumPacketSize: 512,
OriginatorAddress: "127.0.0.1",
OriginatorPort: 2222,
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.XonXoffReqChunkType,
},
func() []byte {
msg := &pssh.XonXoffRequest{
RequestType: ssh.X11RequestType,
WantReply: false,
ClientCanDo: true,
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.XonXoffRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.XonXoffReqChunkType,
},
XonXoffRequest: &pssh.XonXoffRequest{
RequestType: ssh.X11RequestType,
WantReply: false,
ClientCanDo: true,
},
},
},
{
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.UnknownReqChunkType,
},
func() []byte {
msg := &pssh.UnknownRequest{
RequestType: "unknown",
WantReply: false,
Data: []byte("foo"),
}
data, err := proto.Marshal(msg)
require.NoError(t, err)
return data
}(),
&ssh.UnknownRequest{
BaseChunk: &bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.UnknownReqChunkType,
},
UnknownRequest: &pssh.UnknownRequest{
RequestType: "unknown",
WantReply: false,
Data: []byte("foo"),
},
},
},
}
for _, tc := range cases {
t.Run(string(tc.bc.Type), func(t *testing.T) {
got, err := ssh.DecodeChunk(ctx, tc.bc, tc.encoded)
require.NoError(t, err)
assert.Empty(t,
cmp.Diff(
got, tc.want,
cmpopts.IgnoreUnexported(
bsr.BaseChunk{},
pssh.BreakRequest{},
pssh.CancelTCPIPForwardRequest{},
pssh.DirectTCPIPRequest{},
pssh.EnvRequest{},
pssh.ExecRequest{},
pssh.ExitSignalRequest{},
pssh.ExitStatusRequest{},
pssh.ForwardedTCPIPRequest{},
pssh.PtyRequest{},
pssh.SessionRequest{},
pssh.ShellRequest{},
pssh.SignalRequest{},
pssh.SubsystemRequest{},
pssh.TCPIPForwardRequest{},
pssh.WindowChangeRequest{},
pssh.X11ForwardingRequest{},
pssh.X11Request{},
pssh.XonXoffRequest{},
pssh.UnknownRequest{},
),
),
)
})
}
}
func TestDecodeChunkErrors(t *testing.T) {
ctx := context.Background()
cases := []struct {
name string
bc *bsr.BaseChunk
encoded []byte
want error
}{
{
"not-ssh-protocol",
&bsr.BaseChunk{
Protocol: "TEST",
Type: ssh.DataChunkType,
},
[]byte("foo"),
errors.New("ssh.DecodeChunk: invalid protocol TEST"),
},
{
"unsupported-chunk-type",
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: "TEST",
},
[]byte("foo"),
errors.New("ssh.DecodeChunk: unsupported chunk type TEST"),
},
{
"nil-base-chunk",
nil,
[]byte("foo"),
errors.New("ssh.DecodeChunk: nil base chunk: invalid parameter"),
},
{
"no-data",
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.BreakReqChunkType,
},
[]byte(""),
errors.New("ssh.DecodeChunk: not enough data"),
},
{
"invalid-data",
&bsr.BaseChunk{
Protocol: ssh.Protocol,
Type: ssh.BreakReqChunkType,
},
[]byte("foo"),
errors.New("ssh.DecodeChunk: proto: cannot parse invalid wire-format data"),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
_, err := ssh.DecodeChunk(ctx, tc.bc, tc.encoded)
require.EqualError(t, err, tc.want.Error())
})
}
}
@@ -18,7 +18,7 @@ const DirectTCPIPRequestType = "direct-tcpip"
// DirectTCPIPRequest is a chunk to contain data for an SSH Direct TCPIP request
type DirectTCPIPRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.DirectTCPIPRequest
}
@@ -63,7 +63,7 @@ func NewDirectTCPIPRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestam
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &DirectTCPIPRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
DirectTCPIPRequest: &pssh.DirectTCPIPRequest{
RequestType: r.Type,
SenderChannel: sigval.SenderChannel,
+2 -2
View File
@@ -18,7 +18,7 @@ const EnvRequestType = "env"
// EnvRequest is a chunk to contain data for an SSH Env request
type EnvRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.EnvRequest
}
@@ -63,7 +63,7 @@ func NewEnvRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, r *gs
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &EnvRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
EnvRequest: &pssh.EnvRequest{
RequestType: r.Type,
WantReply: r.WantReply,
+2 -2
View File
@@ -18,7 +18,7 @@ const ExecRequestType = "exec"
// ExecRequest is a chunk to contain data for an SSH Exec request
type ExecRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.ExecRequest
}
@@ -63,7 +63,7 @@ func NewExecRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, r *g
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &ExecRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
ExecRequest: &pssh.ExecRequest{
RequestType: r.Type,
WantReply: r.WantReply,
@@ -18,7 +18,7 @@ const ExitSignalRequestType = "exit-signal"
// ExitSignalRequest is a chunk to contain data for an SSH Exit Signal request
type ExitSignalRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.ExitSignalRequest
}
@@ -63,7 +63,7 @@ func NewExitSignalRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestamp
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &ExitSignalRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
ExitSignalRequest: &pssh.ExitSignalRequest{
RequestType: r.Type,
WantReply: r.WantReply,
@@ -18,7 +18,7 @@ const ExitStatusRequestType = "exit-status"
// ExitStatusRequest is a chunk to contain data for an SSH Exit Status request
type ExitStatusRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.ExitStatusRequest
}
@@ -63,7 +63,7 @@ func NewExitStatusRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestamp
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &ExitStatusRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
ExitStatusRequest: &pssh.ExitStatusRequest{
RequestType: r.Type,
WantReply: r.WantReply,
@@ -18,7 +18,7 @@ const ForwardedTCPIPRequestType = "forwarded-tcpip"
// ForwardedTCPIPRequest is a chunk to contain data for an SSH Forwarded TCPIP request
type ForwardedTCPIPRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.ForwardedTCPIPRequest
}
@@ -63,7 +63,7 @@ func NewForwardedTCPIPRequest(ctx context.Context, d bsr.Direction, t *bsr.Times
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &ForwardedTCPIPRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
ForwardedTCPIPRequest: &pssh.ForwardedTCPIPRequest{
RequestType: r.Type,
SenderChannel: sigval.SenderChannel,
+2 -2
View File
@@ -18,7 +18,7 @@ const PtyRequestType = "pty-req"
// PtyPRequest is a chunk to contain data for an SSH Pty request
type PtyRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.PtyRequest
}
@@ -63,7 +63,7 @@ func NewPtyRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, r *gs
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &PtyRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
PtyRequest: &pssh.PtyRequest{
RequestType: r.Type,
WantReply: r.WantReply,
+2 -2
View File
@@ -18,7 +18,7 @@ const SessionRequestType = "session"
// SessionRequest is a chunk to contain data for an SSH Session request
type SessionRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.SessionRequest
}
@@ -63,7 +63,7 @@ func NewSessionRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, r
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &SessionRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
SessionRequest: &pssh.SessionRequest{
RequestType: r.Type,
SenderChannel: sigval.SenderChannel,
+2 -2
View File
@@ -18,7 +18,7 @@ const ShellRequestType = "shell"
// ShellRequest is a chunk to contain data for an SSH Shell request
type ShellRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.ShellRequest
}
@@ -59,7 +59,7 @@ func NewShellRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, r *
}
reqData := &ShellRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
ShellRequest: &pssh.ShellRequest{
RequestType: r.Type,
WantReply: r.WantReply,
+2 -2
View File
@@ -18,7 +18,7 @@ const SignalRequestType = "signal"
// SignalRequest is a chunk to contain data for an SSH Signal request
type SignalRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.SignalRequest
}
@@ -63,7 +63,7 @@ func NewSignalRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, r
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &SignalRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
SignalRequest: &pssh.SignalRequest{
RequestType: r.Type,
WantReply: r.WantReply,
+2 -2
View File
@@ -18,7 +18,7 @@ const SubsystemRequestType = "subsystem"
// SubsystemRequest is a chunk to contain data for an SSH Subsystem request
type SubsystemRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.SubsystemRequest
}
@@ -63,7 +63,7 @@ func NewSubsystemRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestamp,
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &SubsystemRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
SubsystemRequest: &pssh.SubsystemRequest{
RequestType: r.Type,
WantReply: r.WantReply,
@@ -18,7 +18,7 @@ const TCPIPForwardRequestType = "tcpip-forward"
// TCPIPForwardRequest is a chunk to contain data for an SSH TCPIP Forward request
type TCPIPForwardRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.TCPIPForwardRequest
}
@@ -63,7 +63,7 @@ func NewTCPIPForwardRequest(ctx context.Context, d bsr.Direction, t *bsr.Timesta
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &TCPIPForwardRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
TCPIPForwardRequest: &pssh.TCPIPForwardRequest{
RequestType: r.Type,
WantReply: r.WantReply,
+2 -2
View File
@@ -16,7 +16,7 @@ import (
// UnknownRequest is a chunk to contain data for any unrecognized SSH request
type UnknownRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.UnknownRequest
}
@@ -54,7 +54,7 @@ func NewUnknownRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, r
}
return &UnknownRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
UnknownRequest: &pssh.UnknownRequest{
RequestType: r.Type,
WantReply: r.WantReply,
@@ -18,7 +18,7 @@ const WindowChangeRequestType = "window-change"
// WindowChangeRequest is a chunk to contain data for an SSH Window Change request
type WindowChangeRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.WindowChangeRequest
}
@@ -63,7 +63,7 @@ func NewWindowChangeRequest(ctx context.Context, d bsr.Direction, t *bsr.Timesta
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &WindowChangeRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
WindowChangeRequest: &pssh.WindowChangeRequest{
RequestType: r.Type,
WantReply: r.WantReply,
@@ -18,7 +18,7 @@ const X11ForwardingRequestType = "x11-req"
// X11ForwardingRequest is a chunk to contain data for an SSH X11 Forwarding request
type X11ForwardingRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.X11ForwardingRequest
}
@@ -63,7 +63,7 @@ func NewX11ForwardingRequest(ctx context.Context, d bsr.Direction, t *bsr.Timest
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &X11ForwardingRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
X11ForwardingRequest: &pssh.X11ForwardingRequest{
RequestType: r.Type,
WantReply: r.WantReply,
+2 -2
View File
@@ -18,7 +18,7 @@ const X11RequestType = "x11"
// X11Request is a chunk to contain data for an SSH X11 request
type X11Request struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.X11Request
}
@@ -63,7 +63,7 @@ func NewX11Request(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, r *gs
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &X11Request{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
X11Request: &pssh.X11Request{
RequestType: r.Type,
SenderChannel: sigval.SenderChannel,
+2 -2
View File
@@ -18,7 +18,7 @@ const XonXoffRequestType = "xon-xoff"
// XonXoffRequest is a chunk to contain data for an SSH Xon Xoff request
type XonXoffRequest struct {
bsr.BaseChunk
*bsr.BaseChunk
*pssh.XonXoffRequest
}
@@ -63,7 +63,7 @@ func NewXonXoffRequest(ctx context.Context, d bsr.Direction, t *bsr.Timestamp, r
return nil, fmt.Errorf("%s: unable to unmarshal payload: %w", op, err)
}
reqData := &XonXoffRequest{
BaseChunk: *baseChunk,
BaseChunk: baseChunk,
XonXoffRequest: &pssh.XonXoffRequest{
RequestType: r.Type,
WantReply: r.WantReply,