2018-06-17 15:19:49 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2018-06-17 16:49:37 +02:00
|
|
|
"context"
|
2018-06-17 15:19:49 +02:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
|
|
|
|
"github.com/google/gopacket/layers"
|
|
|
|
"github.com/google/gopacket/pcapgo"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
|
|
)
|
|
|
|
|
2018-06-17 17:47:26 +02:00
|
|
|
func handleChannel(newChannel ssh.NewChannel, prb *packetRingBuffer) {
|
2018-06-17 15:19:49 +02:00
|
|
|
if t := newChannel.ChannelType(); t != "session" {
|
|
|
|
newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %q", t))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
channel, requests, err := newChannel.Accept()
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Could not accept channel (%s)", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sessions have out-of-band requests such as "shell", "pty-req" and "env"
|
|
|
|
go func(channel ssh.Channel, requests <-chan *ssh.Request) {
|
|
|
|
s := session{channel: channel}
|
|
|
|
for req := range requests {
|
2018-06-17 17:47:26 +02:00
|
|
|
if err := s.request(req, prb); err != nil {
|
2018-06-17 15:19:49 +02:00
|
|
|
errmsg := []byte(err.Error())
|
|
|
|
// Append a trailing newline; the error message is
|
|
|
|
// displayed as-is by ssh(1).
|
|
|
|
if errmsg[len(errmsg)-1] != '\n' {
|
|
|
|
errmsg = append(errmsg, '\n')
|
|
|
|
}
|
|
|
|
req.Reply(false, errmsg)
|
|
|
|
channel.Write(errmsg)
|
|
|
|
channel.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}(channel, requests)
|
|
|
|
}
|
|
|
|
|
|
|
|
type session struct {
|
|
|
|
channel ssh.Channel
|
|
|
|
}
|
|
|
|
|
2018-06-17 17:47:26 +02:00
|
|
|
func (s *session) request(req *ssh.Request, prb *packetRingBuffer) error {
|
2018-06-17 15:19:49 +02:00
|
|
|
switch req.Type {
|
|
|
|
case "exec":
|
|
|
|
if got, want := len(req.Payload), 4; got < want {
|
|
|
|
return fmt.Errorf("exec request payload too short: got %d, want >= %d", got, want)
|
|
|
|
}
|
|
|
|
log.Printf("exec, wantReply %v, payload %q", req.WantReply, string(req.Payload[4:]))
|
|
|
|
|
2018-06-17 16:49:37 +02:00
|
|
|
ctx, canc := context.WithCancel(context.Background())
|
|
|
|
defer canc()
|
|
|
|
|
2018-06-17 15:19:49 +02:00
|
|
|
pcapw := pcapgo.NewWriter(s.channel)
|
|
|
|
if err := pcapw.WriteFileHeader(1600, layers.LinkTypeEthernet); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-06-17 17:47:26 +02:00
|
|
|
prb.Lock()
|
|
|
|
packets, err := capturePackets(ctx)
|
|
|
|
buffered := prb.packetsLocked()
|
|
|
|
prb.Unlock()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-06-17 15:19:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
req.Reply(true, nil)
|
|
|
|
|
2018-06-17 17:47:26 +02:00
|
|
|
for _, packet := range buffered {
|
|
|
|
if err := pcapw.WritePacket(packet.Metadata().CaptureInfo, packet.Data()); err != nil {
|
|
|
|
return fmt.Errorf("pcap.WritePacket(): %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-17 15:19:49 +02:00
|
|
|
for packet := range packets {
|
|
|
|
if err := pcapw.WritePacket(packet.Metadata().CaptureInfo, packet.Data()); err != nil {
|
|
|
|
return fmt.Errorf("pcap.WritePacket(): %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unknown request type: %q", req.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadHostKey(path string) (ssh.Signer, error) {
|
|
|
|
b, err := ioutil.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ssh.ParsePrivateKey(b)
|
|
|
|
}
|
|
|
|
|
2018-06-27 19:44:39 +02:00
|
|
|
type server struct {
|
|
|
|
config *ssh.ServerConfig
|
|
|
|
prb *packetRingBuffer
|
|
|
|
}
|
|
|
|
|
|
|
|
func newServer(prb *packetRingBuffer) (*server, error) {
|
2018-06-17 15:19:49 +02:00
|
|
|
config := &ssh.ServerConfig{
|
|
|
|
PublicKeyCallback: func(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
|
|
|
|
return nil, nil // authorize all users
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
signer, err := loadHostKey(*hostKeyPath)
|
|
|
|
if err != nil {
|
2018-06-27 19:44:39 +02:00
|
|
|
return nil, err
|
2018-06-17 15:19:49 +02:00
|
|
|
}
|
|
|
|
config.AddHostKey(signer)
|
|
|
|
|
2018-06-27 19:44:39 +02:00
|
|
|
return &server{
|
|
|
|
config: config,
|
|
|
|
prb: prb,
|
|
|
|
}, nil
|
|
|
|
}
|
2018-06-17 15:19:49 +02:00
|
|
|
|
2018-06-27 19:44:39 +02:00
|
|
|
func (s *server) listenerFor(host string) *serverListener {
|
|
|
|
return &serverListener{srv: s, host: host}
|
|
|
|
}
|
2018-06-17 15:19:49 +02:00
|
|
|
|
2018-06-27 19:44:39 +02:00
|
|
|
type serverListener struct {
|
|
|
|
srv *server
|
|
|
|
host string
|
|
|
|
ln net.Listener
|
|
|
|
}
|
2018-06-17 15:19:49 +02:00
|
|
|
|
2018-06-27 19:44:39 +02:00
|
|
|
func (sl *serverListener) ListenAndServe() error {
|
|
|
|
ln, err := net.Listen("tcp", net.JoinHostPort(sl.host, "5022"))
|
2018-06-17 15:19:49 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-06-27 19:44:39 +02:00
|
|
|
sl.ln = ln
|
|
|
|
for {
|
|
|
|
conn, err := ln.Accept()
|
2018-06-17 15:19:49 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-06-27 19:44:39 +02:00
|
|
|
go func(conn net.Conn) {
|
|
|
|
_, chans, reqs, err := ssh.NewServerConn(conn, sl.srv.config)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("handshake: %v", err)
|
|
|
|
return
|
|
|
|
}
|
2018-06-17 15:19:49 +02:00
|
|
|
|
2018-06-27 19:44:39 +02:00
|
|
|
// discard all out of band requests
|
|
|
|
go ssh.DiscardRequests(reqs)
|
2018-06-17 15:19:49 +02:00
|
|
|
|
2018-06-27 19:44:39 +02:00
|
|
|
for newChannel := range chans {
|
|
|
|
handleChannel(newChannel, sl.srv.prb)
|
|
|
|
}
|
|
|
|
}(conn)
|
|
|
|
}
|
2018-06-17 15:19:49 +02:00
|
|
|
return nil
|
|
|
|
}
|
2018-06-27 19:44:39 +02:00
|
|
|
|
|
|
|
func (sl *serverListener) Close() error {
|
|
|
|
return sl.ln.Close()
|
|
|
|
}
|