Compare commits
	
		
			21 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					612acfddff | ||
| 
						 | 
					932b80d4f7 | ||
| 
						 | 
					fac5f69ad2 | ||
| 
						 | 
					97c944bb63 | ||
| 
						 | 
					d0c4fe78ee | ||
| 
						 | 
					265457b451 | ||
| 
						 | 
					4a4a29c9f6 | ||
| 
						 | 
					0a91b9e1c9 | ||
| 
						 | 
					f56163295c | ||
| 
						 | 
					d1c87c068b | ||
| 
						 | 
					fa20761110 | ||
| 
						 | 
					e4a0e0a0e9 | ||
| 
						 | 
					d30ae19e2a | ||
| 
						 | 
					5c919e6bff | ||
| 
						 | 
					434393d1c3 | ||
| 
						 | 
					af9aa5d7cb | ||
| 
						 | 
					05eb75442a | ||
| 
						 | 
					3496ed0c7e | ||
| 
						 | 
					1b89604c7a | ||
| 
						 | 
					67a9d133e9 | ||
| 
						 | 
					ed9118b346 | 
@@ -54,7 +54,7 @@ See https://github.com/42wim/matterbridge/wiki
 | 
			
		||||
 | 
			
		||||
# Installing
 | 
			
		||||
## Binaries
 | 
			
		||||
* Latest stable release [v1.5.0](https://github.com/42wim/matterbridge/releases/latest)
 | 
			
		||||
* Latest stable release [v1.6.1](https://github.com/42wim/matterbridge/releases/latest)
 | 
			
		||||
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)  
 | 
			
		||||
 | 
			
		||||
## Building
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
@@ -8,14 +9,13 @@ import (
 | 
			
		||||
	"github.com/zfjagann/golang-ring"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Api struct {
 | 
			
		||||
	Config   *config.Protocol
 | 
			
		||||
	Remote   chan config.Message
 | 
			
		||||
	Account  string
 | 
			
		||||
	Messages ring.Ring
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ApiMessage struct {
 | 
			
		||||
@@ -33,23 +33,21 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Api {
 | 
			
		||||
	b := &Api{}
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Api {
 | 
			
		||||
	b := &Api{BridgeConfig: cfg}
 | 
			
		||||
	e := echo.New()
 | 
			
		||||
	b.Messages = ring.Ring{}
 | 
			
		||||
	b.Messages.SetCapacity(cfg.Buffer)
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Messages.SetCapacity(b.Config.Buffer)
 | 
			
		||||
	if b.Config.Token != "" {
 | 
			
		||||
		e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) {
 | 
			
		||||
			return key == b.Config.Token, nil
 | 
			
		||||
		}))
 | 
			
		||||
	}
 | 
			
		||||
	e.GET("/api/messages", b.handleMessages)
 | 
			
		||||
	e.GET("/api/stream", b.handleStream)
 | 
			
		||||
	e.POST("/api/message", b.handlePostMessage)
 | 
			
		||||
	go func() {
 | 
			
		||||
		flog.Fatal(e.Start(cfg.BindAddress))
 | 
			
		||||
		flog.Fatal(e.Start(b.Config.BindAddress))
 | 
			
		||||
	}()
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
@@ -103,3 +101,24 @@ func (b *Api) handleMessages(c echo.Context) error {
 | 
			
		||||
	b.Messages = ring.Ring{}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Api) handleStream(c echo.Context) error {
 | 
			
		||||
	c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
 | 
			
		||||
	c.Response().WriteHeader(http.StatusOK)
 | 
			
		||||
	closeNotifier := c.Response().CloseNotify()
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-closeNotifier:
 | 
			
		||||
			return nil
 | 
			
		||||
		default:
 | 
			
		||||
			msg := b.Messages.Dequeue()
 | 
			
		||||
			if msg != nil {
 | 
			
		||||
				if err := json.NewEncoder(c.Response()).Encode(msg); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				c.Response().Flush()
 | 
			
		||||
			}
 | 
			
		||||
			time.Sleep(200 * time.Millisecond)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/mattermost"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/rocketchat"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/slack"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/sshchat"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/steam"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/telegram"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/xmpp"
 | 
			
		||||
@@ -45,44 +46,49 @@ func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Brid
 | 
			
		||||
	b.Protocol = protocol
 | 
			
		||||
	b.Account = bridge.Account
 | 
			
		||||
	b.Joined = make(map[string]bool)
 | 
			
		||||
	bridgeConfig := &config.BridgeConfig{General: &cfg.General, Account: bridge.Account, Remote: c}
 | 
			
		||||
 | 
			
		||||
	// override config from environment
 | 
			
		||||
	config.OverrideCfgFromEnv(cfg, protocol, name)
 | 
			
		||||
	switch protocol {
 | 
			
		||||
	case "mattermost":
 | 
			
		||||
		b.Config = cfg.Mattermost[name]
 | 
			
		||||
		b.Bridger = bmattermost.New(cfg.Mattermost[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Mattermost[name]
 | 
			
		||||
		b.Bridger = bmattermost.New(bridgeConfig)
 | 
			
		||||
	case "irc":
 | 
			
		||||
		b.Config = cfg.IRC[name]
 | 
			
		||||
		b.Bridger = birc.New(cfg.IRC[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.IRC[name]
 | 
			
		||||
		b.Bridger = birc.New(bridgeConfig)
 | 
			
		||||
	case "gitter":
 | 
			
		||||
		b.Config = cfg.Gitter[name]
 | 
			
		||||
		b.Bridger = bgitter.New(cfg.Gitter[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Gitter[name]
 | 
			
		||||
		b.Bridger = bgitter.New(bridgeConfig)
 | 
			
		||||
	case "slack":
 | 
			
		||||
		b.Config = cfg.Slack[name]
 | 
			
		||||
		b.Bridger = bslack.New(cfg.Slack[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Slack[name]
 | 
			
		||||
		b.Bridger = bslack.New(bridgeConfig)
 | 
			
		||||
	case "xmpp":
 | 
			
		||||
		b.Config = cfg.Xmpp[name]
 | 
			
		||||
		b.Bridger = bxmpp.New(cfg.Xmpp[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Xmpp[name]
 | 
			
		||||
		b.Bridger = bxmpp.New(bridgeConfig)
 | 
			
		||||
	case "discord":
 | 
			
		||||
		b.Config = cfg.Discord[name]
 | 
			
		||||
		b.Bridger = bdiscord.New(cfg.Discord[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Discord[name]
 | 
			
		||||
		b.Bridger = bdiscord.New(bridgeConfig)
 | 
			
		||||
	case "telegram":
 | 
			
		||||
		b.Config = cfg.Telegram[name]
 | 
			
		||||
		b.Bridger = btelegram.New(cfg.Telegram[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Telegram[name]
 | 
			
		||||
		b.Bridger = btelegram.New(bridgeConfig)
 | 
			
		||||
	case "rocketchat":
 | 
			
		||||
		b.Config = cfg.Rocketchat[name]
 | 
			
		||||
		b.Bridger = brocketchat.New(cfg.Rocketchat[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Rocketchat[name]
 | 
			
		||||
		b.Bridger = brocketchat.New(bridgeConfig)
 | 
			
		||||
	case "matrix":
 | 
			
		||||
		b.Config = cfg.Matrix[name]
 | 
			
		||||
		b.Bridger = bmatrix.New(cfg.Matrix[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Matrix[name]
 | 
			
		||||
		b.Bridger = bmatrix.New(bridgeConfig)
 | 
			
		||||
	case "steam":
 | 
			
		||||
		b.Config = cfg.Steam[name]
 | 
			
		||||
		b.Bridger = bsteam.New(cfg.Steam[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Steam[name]
 | 
			
		||||
		b.Bridger = bsteam.New(bridgeConfig)
 | 
			
		||||
	case "sshchat":
 | 
			
		||||
		bridgeConfig.Config = cfg.Sshchat[name]
 | 
			
		||||
		b.Bridger = bsshchat.New(bridgeConfig)
 | 
			
		||||
	case "api":
 | 
			
		||||
		b.Config = cfg.Api[name]
 | 
			
		||||
		b.Bridger = api.New(cfg.Api[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Api[name]
 | 
			
		||||
		b.Bridger = api.New(bridgeConfig)
 | 
			
		||||
	}
 | 
			
		||||
	b.Config = bridgeConfig.Config
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -60,6 +60,7 @@ type Protocol struct {
 | 
			
		||||
	IgnoreMessages         string // all protocols
 | 
			
		||||
	Jid                    string // xmpp
 | 
			
		||||
	Login                  string // mattermost, matrix
 | 
			
		||||
	MediaDownloadSize      int    // all protocols
 | 
			
		||||
	MediaServerDownload    string
 | 
			
		||||
	MediaServerUpload      string
 | 
			
		||||
	MessageDelay           int        // IRC, time in millisecond to wait between messages
 | 
			
		||||
@@ -80,6 +81,7 @@ type Protocol struct {
 | 
			
		||||
	Password               string     // IRC,mattermost,XMPP,matrix
 | 
			
		||||
	PrefixMessagesWithNick bool       // mattemost, slack
 | 
			
		||||
	Protocol               string     // all protocols
 | 
			
		||||
	RejoinDelay            int        // IRC
 | 
			
		||||
	ReplaceMessages        [][]string // all protocols
 | 
			
		||||
	ReplaceNicks           [][]string // all protocols
 | 
			
		||||
	RemoteNickFormat       string     // all protocols
 | 
			
		||||
@@ -141,11 +143,19 @@ type Config struct {
 | 
			
		||||
	Discord            map[string]Protocol
 | 
			
		||||
	Telegram           map[string]Protocol
 | 
			
		||||
	Rocketchat         map[string]Protocol
 | 
			
		||||
	Sshchat            map[string]Protocol
 | 
			
		||||
	General            Protocol
 | 
			
		||||
	Gateway            []Gateway
 | 
			
		||||
	SameChannelGateway []SameChannelGateway
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type BridgeConfig struct {
 | 
			
		||||
	Config  Protocol
 | 
			
		||||
	General *Protocol
 | 
			
		||||
	Account string
 | 
			
		||||
	Remote  chan Message
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewConfig(cfgfile string) *Config {
 | 
			
		||||
	var cfg Config
 | 
			
		||||
	if _, err := toml.DecodeFile(cfgfile, &cfg); err != nil {
 | 
			
		||||
@@ -173,6 +183,9 @@ func NewConfig(cfgfile string) *Config {
 | 
			
		||||
	if fail {
 | 
			
		||||
		log.Fatalf("Fix your config. Please see changelog for more information")
 | 
			
		||||
	}
 | 
			
		||||
	if cfg.General.MediaDownloadSize == 0 {
 | 
			
		||||
		cfg.General.MediaDownloadSize = 1000000
 | 
			
		||||
	}
 | 
			
		||||
	return &cfg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,9 +12,6 @@ import (
 | 
			
		||||
 | 
			
		||||
type bdiscord struct {
 | 
			
		||||
	c              *discordgo.Session
 | 
			
		||||
	Config         *config.Protocol
 | 
			
		||||
	Remote         chan config.Message
 | 
			
		||||
	Account        string
 | 
			
		||||
	Channels       []*discordgo.Channel
 | 
			
		||||
	Nick           string
 | 
			
		||||
	UseChannelID   bool
 | 
			
		||||
@@ -24,6 +21,7 @@ type bdiscord struct {
 | 
			
		||||
	webhookToken   string
 | 
			
		||||
	channelInfoMap map[string]*config.ChannelInfo
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -33,11 +31,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *bdiscord {
 | 
			
		||||
	b := &bdiscord{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
func New(cfg *config.BridgeConfig) *bdiscord {
 | 
			
		||||
	b := &bdiscord{BridgeConfig: cfg}
 | 
			
		||||
	b.userMemberMap = make(map[string]*discordgo.Member)
 | 
			
		||||
	b.channelInfoMap = make(map[string]*config.ChannelInfo)
 | 
			
		||||
	if b.Config.WebhookURL != "" {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,13 +9,11 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Bgitter struct {
 | 
			
		||||
	c       *gitter.Gitter
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
	User    *gitter.User
 | 
			
		||||
	Users   []gitter.User
 | 
			
		||||
	Rooms   []gitter.Room
 | 
			
		||||
	c     *gitter.Gitter
 | 
			
		||||
	User  *gitter.User
 | 
			
		||||
	Users []gitter.User
 | 
			
		||||
	Rooms []gitter.Room
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -25,12 +23,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bgitter {
 | 
			
		||||
	b := &Bgitter{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	return b
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bgitter {
 | 
			
		||||
	return &Bgitter{BridgeConfig: cfg}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bgitter) Connect() error {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,12 +25,11 @@ type Birc struct {
 | 
			
		||||
	i               *girc.Client
 | 
			
		||||
	Nick            string
 | 
			
		||||
	names           map[string][]string
 | 
			
		||||
	Config          *config.Protocol
 | 
			
		||||
	Remote          chan config.Message
 | 
			
		||||
	connected       chan struct{}
 | 
			
		||||
	Local           chan config.Message // local queue for flood control
 | 
			
		||||
	Account         string
 | 
			
		||||
	FirstConnection bool
 | 
			
		||||
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -40,13 +39,11 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Birc {
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Birc {
 | 
			
		||||
	b := &Birc{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.BridgeConfig = cfg
 | 
			
		||||
	b.Nick = b.Config.Nick
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.names = make(map[string][]string)
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.connected = make(chan struct{})
 | 
			
		||||
	if b.Config.MessageDelay == 0 {
 | 
			
		||||
		b.Config.MessageDelay = 1300
 | 
			
		||||
@@ -197,8 +194,9 @@ func (b *Birc) Send(msg config.Message) (string, error) {
 | 
			
		||||
		msg.Text = helper.SplitStringLength(msg.Text, b.Config.MessageLength)
 | 
			
		||||
	}
 | 
			
		||||
	for _, text := range strings.Split(msg.Text, "\n") {
 | 
			
		||||
		input := []rune(text)
 | 
			
		||||
		if len(text) > b.Config.MessageLength {
 | 
			
		||||
			text = text[:b.Config.MessageLength] + " <message clipped>"
 | 
			
		||||
			text = string(input[:b.Config.MessageLength]) + " <message clipped>"
 | 
			
		||||
		}
 | 
			
		||||
		if len(b.Local) < b.Config.MessageQueue {
 | 
			
		||||
			if len(b.Local) == b.Config.MessageQueue-1 {
 | 
			
		||||
@@ -269,6 +267,7 @@ func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) {
 | 
			
		||||
	channel := event.Params[0]
 | 
			
		||||
	if event.Command == "KICK" {
 | 
			
		||||
		flog.Infof("Got kicked from %s by %s", channel, event.Source.Name)
 | 
			
		||||
		time.Sleep(time.Duration(b.Config.RejoinDelay) * time.Second)
 | 
			
		||||
		b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: channel, Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -327,11 +326,10 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
 | 
			
		||||
	rmsg := config.Message{Username: event.Source.Name, Channel: event.Params[0], Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host}
 | 
			
		||||
	flog.Debugf("handlePrivMsg() %s %s %#v", event.Source.Name, event.Trailing, event)
 | 
			
		||||
	msg := ""
 | 
			
		||||
	if event.Command == "CTCP_ACTION" {
 | 
			
		||||
		//	msg = event.Source.Name + " "
 | 
			
		||||
	if event.IsAction() {
 | 
			
		||||
		rmsg.Event = config.EVENT_USER_ACTION
 | 
			
		||||
	}
 | 
			
		||||
	msg += event.Trailing
 | 
			
		||||
	msg += event.StripAction()
 | 
			
		||||
	// strip IRC colors
 | 
			
		||||
	re := regexp.MustCompile(`[[:cntrl:]](?:\d{1,2}(?:,\d{1,2})?)?`)
 | 
			
		||||
	msg = re.ReplaceAllString(msg, "")
 | 
			
		||||
 
 | 
			
		||||
@@ -15,12 +15,10 @@ import (
 | 
			
		||||
 | 
			
		||||
type Bmatrix struct {
 | 
			
		||||
	mc      *matrix.Client
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
	UserID  string
 | 
			
		||||
	RoomMap map[string]string
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -30,12 +28,9 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bmatrix {
 | 
			
		||||
	b := &Bmatrix{}
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bmatrix {
 | 
			
		||||
	b := &Bmatrix{BridgeConfig: cfg}
 | 
			
		||||
	b.RoomMap = make(map[string]string)
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -106,6 +101,7 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
			
		||||
					res, err := b.mc.UploadToContentRepo(content, mtype, int64(len(*fi.Data)))
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						flog.Errorf("file upload failed: %#v", err)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					if strings.Contains(mtype, "video") {
 | 
			
		||||
						flog.Debugf("sendVideo %s", res.ContentURI)
 | 
			
		||||
@@ -179,7 +175,7 @@ func (b *Bmatrix) handlematrix() error {
 | 
			
		||||
				size := info["size"].(float64)
 | 
			
		||||
				name := ev.Content["body"].(string)
 | 
			
		||||
				flog.Debugf("trying to download %#v with size %#v", name, size)
 | 
			
		||||
				if size <= 1000000 {
 | 
			
		||||
				if size <= float64(b.General.MediaDownloadSize) {
 | 
			
		||||
					data, err := helper.DownloadFile(url)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						flog.Errorf("download %s failed %#v", url, err)
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@ type Bmattermost struct {
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	TeamId  string
 | 
			
		||||
	Account string
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -45,11 +46,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bmattermost {
 | 
			
		||||
	b := &Bmattermost{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bmattermost {
 | 
			
		||||
	b := &Bmattermost{BridgeConfig: cfg}
 | 
			
		||||
	b.mmMap = make(map[string]string)
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,9 +14,7 @@ type MMhook struct {
 | 
			
		||||
 | 
			
		||||
type Brocketchat struct {
 | 
			
		||||
	MMhook
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -26,12 +24,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Brocketchat {
 | 
			
		||||
	b := &Brocketchat{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	return b
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Brocketchat {
 | 
			
		||||
	return &Brocketchat{BridgeConfig: cfg}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Brocketchat) Command(cmd string) string {
 | 
			
		||||
 
 | 
			
		||||
@@ -27,14 +27,12 @@ type MMMessage struct {
 | 
			
		||||
type Bslack struct {
 | 
			
		||||
	mh       *matterhook.Client
 | 
			
		||||
	sc       *slack.Client
 | 
			
		||||
	Config   *config.Protocol
 | 
			
		||||
	rtm      *slack.RTM
 | 
			
		||||
	Plus     bool
 | 
			
		||||
	Remote   chan config.Message
 | 
			
		||||
	Users    []slack.User
 | 
			
		||||
	Account  string
 | 
			
		||||
	si       *slack.Info
 | 
			
		||||
	channels []slack.Channel
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -44,12 +42,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bslack {
 | 
			
		||||
	b := &Bslack{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	return b
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bslack {
 | 
			
		||||
	return &Bslack{BridgeConfig: cfg}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bslack) Command(cmd string) string {
 | 
			
		||||
@@ -161,7 +155,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
 | 
			
		||||
		np.AsUser = true
 | 
			
		||||
	}
 | 
			
		||||
	np.Username = nick
 | 
			
		||||
	np.IconURL = config.GetIconURL(&msg, b.Config)
 | 
			
		||||
	np.IconURL = config.GetIconURL(&msg, &b.Config)
 | 
			
		||||
	if msg.Avatar != "" {
 | 
			
		||||
		np.IconURL = msg.Avatar
 | 
			
		||||
	}
 | 
			
		||||
@@ -294,7 +288,7 @@ func (b *Bslack) handleSlack() {
 | 
			
		||||
		// if we have a file attached, download it (in memory) and put a pointer to it in msg.Extra
 | 
			
		||||
		if message.Raw.File != nil {
 | 
			
		||||
			// limit to 1MB for now
 | 
			
		||||
			if message.Raw.File.Size <= 1000000 {
 | 
			
		||||
			if message.Raw.File.Size <= b.General.MediaDownloadSize {
 | 
			
		||||
				comment := ""
 | 
			
		||||
				data, err := b.downloadFile(message.Raw.File.URLPrivateDownload)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										132
									
								
								bridge/sshchat/sshchat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								bridge/sshchat/sshchat.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
			
		||||
package bsshchat
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/shazow/ssh-chat/sshd"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Bsshchat struct {
 | 
			
		||||
	r *bufio.Scanner
 | 
			
		||||
	w io.WriteCloser
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
var protocol = "sshchat"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bsshchat {
 | 
			
		||||
	return &Bsshchat{BridgeConfig: cfg}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bsshchat) Connect() error {
 | 
			
		||||
	var err error
 | 
			
		||||
	flog.Infof("Connecting %s", b.Config.Server)
 | 
			
		||||
	go func() {
 | 
			
		||||
		err = sshd.ConnectShell(b.Config.Server, b.Config.Nick, func(r io.Reader, w io.WriteCloser) error {
 | 
			
		||||
			b.r = bufio.NewScanner(r)
 | 
			
		||||
			b.w = w
 | 
			
		||||
			b.r.Scan()
 | 
			
		||||
			w.Write([]byte("/theme mono\r\n"))
 | 
			
		||||
			b.handleSshChat()
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
	}()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		flog.Debugf("%#v", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	flog.Info("Connection succeeded")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bsshchat) Disconnect() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bsshchat) JoinChannel(channel config.ChannelInfo) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bsshchat) Send(msg config.Message) (string, error) {
 | 
			
		||||
	// ignore delete messages
 | 
			
		||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	flog.Debugf("Receiving %#v", msg)
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.URL != "" {
 | 
			
		||||
					msg.Text = fi.URL
 | 
			
		||||
				}
 | 
			
		||||
				b.w.Write([]byte(msg.Username + msg.Text))
 | 
			
		||||
			}
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	b.w.Write([]byte(msg.Username + msg.Text + "\r\n"))
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
func (b *Bsshchat) sshchatKeepAlive() chan bool {
 | 
			
		||||
	done := make(chan bool)
 | 
			
		||||
	go func() {
 | 
			
		||||
		ticker := time.NewTicker(90 * time.Second)
 | 
			
		||||
		defer ticker.Stop()
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case <-ticker.C:
 | 
			
		||||
				flog.Debugf("PING")
 | 
			
		||||
				err := b.xc.PingC2S("", "")
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					flog.Debugf("PING failed %#v", err)
 | 
			
		||||
				}
 | 
			
		||||
			case <-done:
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	return done
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
func stripPrompt(s string) string {
 | 
			
		||||
	pos := strings.LastIndex(s, "\033[K")
 | 
			
		||||
	if pos < 0 {
 | 
			
		||||
		return s
 | 
			
		||||
	}
 | 
			
		||||
	return s[pos+3:]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bsshchat) handleSshChat() error {
 | 
			
		||||
	/*
 | 
			
		||||
		done := b.sshchatKeepAlive()
 | 
			
		||||
		defer close(done)
 | 
			
		||||
	*/
 | 
			
		||||
	wait := true
 | 
			
		||||
	for {
 | 
			
		||||
		if b.r.Scan() {
 | 
			
		||||
			res := strings.Split(stripPrompt(b.r.Text()), ":")
 | 
			
		||||
			if res[0] == "-> Set theme" {
 | 
			
		||||
				wait = false
 | 
			
		||||
				log.Debugf("mono found, allowing")
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if !wait {
 | 
			
		||||
				flog.Debugf("message %#v", res)
 | 
			
		||||
				rmsg := config.Message{Username: res[0], Text: strings.Join(res[1:], ":"), Channel: "sshchat", Account: b.Account, UserID: "nick"}
 | 
			
		||||
				b.Remote <- rmsg
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -16,11 +16,9 @@ import (
 | 
			
		||||
type Bsteam struct {
 | 
			
		||||
	c         *steam.Client
 | 
			
		||||
	connected chan struct{}
 | 
			
		||||
	Config    *config.Protocol
 | 
			
		||||
	Remote    chan config.Message
 | 
			
		||||
	Account   string
 | 
			
		||||
	userMap   map[steamid.SteamId]string
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -30,11 +28,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bsteam {
 | 
			
		||||
	b := &Bsteam{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bsteam {
 | 
			
		||||
	b := &Bsteam{BridgeConfig: cfg}
 | 
			
		||||
	b.userMap = make(map[steamid.SteamId]string)
 | 
			
		||||
	b.connected = make(chan struct{})
 | 
			
		||||
	return b
 | 
			
		||||
 
 | 
			
		||||
@@ -12,10 +12,8 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Btelegram struct {
 | 
			
		||||
	c       *tgbotapi.BotAPI
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
	c *tgbotapi.BotAPI
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -25,12 +23,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Btelegram {
 | 
			
		||||
	b := &Btelegram{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	return b
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Btelegram {
 | 
			
		||||
	return &Btelegram{BridgeConfig: cfg}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Btelegram) Connect() error {
 | 
			
		||||
@@ -90,6 +84,9 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		m := tgbotapi.NewEditMessageText(chatid, msgid, msg.Username+msg.Text)
 | 
			
		||||
		if b.Config.MessageFormat == "HTML" {
 | 
			
		||||
			m.ParseMode = tgbotapi.ModeHTML
 | 
			
		||||
		}
 | 
			
		||||
		_, err = b.c.Send(m)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
@@ -179,6 +176,29 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
 | 
			
		||||
		if message.Document != nil {
 | 
			
		||||
			b.handleDownload(message.Document, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
		if message.Voice != nil {
 | 
			
		||||
			b.handleDownload(message.Voice, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
		if message.Audio != nil {
 | 
			
		||||
			b.handleDownload(message.Audio, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if message.ForwardFrom != nil {
 | 
			
		||||
			usernameForward := ""
 | 
			
		||||
			if b.Config.UseFirstName {
 | 
			
		||||
				usernameForward = message.ForwardFrom.FirstName
 | 
			
		||||
			}
 | 
			
		||||
			if usernameForward == "" {
 | 
			
		||||
				usernameForward = message.ForwardFrom.UserName
 | 
			
		||||
				if usernameForward == "" {
 | 
			
		||||
					usernameForward = message.ForwardFrom.FirstName
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if usernameForward == "" {
 | 
			
		||||
				usernameForward = "unknown"
 | 
			
		||||
			}
 | 
			
		||||
			text = "Forwarded from " + usernameForward + ": " + text
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// quote the previous message
 | 
			
		||||
		if message.ReplyToMessage != nil {
 | 
			
		||||
@@ -224,6 +244,23 @@ func (b *Btelegram) handleDownload(file interface{}, msg *config.Message) {
 | 
			
		||||
	text := ""
 | 
			
		||||
	fileid := ""
 | 
			
		||||
	switch v := file.(type) {
 | 
			
		||||
	case *tgbotapi.Audio:
 | 
			
		||||
		size = v.FileSize
 | 
			
		||||
		url = b.getFileDirectURL(v.FileID)
 | 
			
		||||
		urlPart := strings.Split(url, "/")
 | 
			
		||||
		name = urlPart[len(urlPart)-1]
 | 
			
		||||
		text = " " + url
 | 
			
		||||
		fileid = v.FileID
 | 
			
		||||
	case *tgbotapi.Voice:
 | 
			
		||||
		size = v.FileSize
 | 
			
		||||
		url = b.getFileDirectURL(v.FileID)
 | 
			
		||||
		urlPart := strings.Split(url, "/")
 | 
			
		||||
		name = urlPart[len(urlPart)-1]
 | 
			
		||||
		text = " " + url
 | 
			
		||||
		if !strings.HasSuffix(name, ".ogg") {
 | 
			
		||||
			name = name + ".ogg"
 | 
			
		||||
		}
 | 
			
		||||
		fileid = v.FileID
 | 
			
		||||
	case *tgbotapi.Sticker:
 | 
			
		||||
		size = v.FileSize
 | 
			
		||||
		url = b.getFileDirectURL(v.FileID)
 | 
			
		||||
@@ -262,7 +299,7 @@ func (b *Btelegram) handleDownload(file interface{}, msg *config.Message) {
 | 
			
		||||
	// if we have a file attached, download it (in memory) and put a pointer to it in msg.Extra
 | 
			
		||||
	// limit to 1MB for now
 | 
			
		||||
	flog.Debugf("trying to download %#v fileid %#v with size %#v", name, fileid, size)
 | 
			
		||||
	if size <= 1000000 {
 | 
			
		||||
	if size <= b.General.MediaDownloadSize {
 | 
			
		||||
		data, err := helper.DownloadFile(url)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			flog.Errorf("download %s failed %#v", url, err)
 | 
			
		||||
 
 | 
			
		||||
@@ -14,9 +14,7 @@ import (
 | 
			
		||||
type Bxmpp struct {
 | 
			
		||||
	xc      *xmpp.Client
 | 
			
		||||
	xmppMap map[string]string
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -26,12 +24,9 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bxmpp {
 | 
			
		||||
	b := &Bxmpp{}
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bxmpp {
 | 
			
		||||
	b := &Bxmpp{BridgeConfig: cfg}
 | 
			
		||||
	b.xmppMap = make(map[string]string)
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,3 +1,26 @@
 | 
			
		||||
# v1.6.1
 | 
			
		||||
## Bugfix
 | 
			
		||||
* general: Display of nicks not longer working (regression). Closes #323
 | 
			
		||||
 | 
			
		||||
# v1.6.0
 | 
			
		||||
## New features
 | 
			
		||||
* sshchat: New protocol support added (https://github.com/shazow/ssh-chat)
 | 
			
		||||
* general: Allow specifying maximum download size of media using MediaDownloadSize (slack,telegram,matrix)
 | 
			
		||||
* api: Add (simple, one listener) long-polling support (api). Closes #307
 | 
			
		||||
* telegram: Add support for forwarded messages. Closes #313
 | 
			
		||||
* telegram: Add support for Audio/Voice files (telegram). Closes #314
 | 
			
		||||
* irc: Add RejoinDelay option. Delay to rejoin after channel kick (irc). Closes #322
 | 
			
		||||
 | 
			
		||||
## Bugfix
 | 
			
		||||
* telegram: Also use HTML in edited messages (telegram). Closes #315
 | 
			
		||||
* matrix: Fix panic (matrix). Closes #316
 | 
			
		||||
 | 
			
		||||
# v1.5.1
 | 
			
		||||
 | 
			
		||||
## Bugfix
 | 
			
		||||
* irc: Fix irc ACTION regression (irc). Closes #306
 | 
			
		||||
* irc: Split on UTF-8 for MessageSplit (irc). Closes #308
 | 
			
		||||
 | 
			
		||||
# v1.5.0
 | 
			
		||||
## New features
 | 
			
		||||
* general: remote mediaserver support. See MediaServerDownload and MediaServerUpload in matterbridge.toml.sample
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	version = "1.5.0"
 | 
			
		||||
	version = "1.6.1"
 | 
			
		||||
	githash string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -85,6 +85,10 @@ MessageLength=400
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
MessageSplit=false
 | 
			
		||||
 | 
			
		||||
#Delay in seconds to rejoin a channel when kicked
 | 
			
		||||
#OPTIONAL (default 0)
 | 
			
		||||
RejoinDelay=0
 | 
			
		||||
 | 
			
		||||
#Nicks you want to ignore. 
 | 
			
		||||
#Messages from those users will not be sent to other bridges.
 | 
			
		||||
#OPTIONAL
 | 
			
		||||
@@ -1044,6 +1048,15 @@ MediaServerUpload="https://user:pass@yourserver.com/upload"
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
MediaServerDownload="https://youserver.com/download"
 | 
			
		||||
 | 
			
		||||
#MediaDownloadSize is the maximum size of attachments, videos, images
 | 
			
		||||
#matterbridge will download and upload this file to bridges that also support uploading files.
 | 
			
		||||
#eg downloading from slack to upload it to mattermost
 | 
			
		||||
#
 | 
			
		||||
#It will only download from bridges that don't have public links available, which are for the moment
 | 
			
		||||
#slack, telegram and matrix
 | 
			
		||||
#
 | 
			
		||||
#Optional (default 1000000 (1 megabyte))
 | 
			
		||||
MediaDownloadSize=1000000
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#Gateway configuration
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/github.com/labstack/echo/context.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/labstack/echo/context.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -494,14 +494,9 @@ func (c *context) Stream(code int, contentType string, r io.Reader) (err error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *context) File(file string) (err error) {
 | 
			
		||||
	file, err = url.QueryUnescape(file) // Issue #839
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f, err := os.Open(file)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ErrNotFound
 | 
			
		||||
		return NotFoundHandler(c)
 | 
			
		||||
	}
 | 
			
		||||
	defer f.Close()
 | 
			
		||||
 | 
			
		||||
@@ -510,7 +505,7 @@ func (c *context) File(file string) (err error) {
 | 
			
		||||
		file = filepath.Join(file, indexPage)
 | 
			
		||||
		f, err = os.Open(file)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return ErrNotFound
 | 
			
		||||
			return NotFoundHandler(c)
 | 
			
		||||
		}
 | 
			
		||||
		defer f.Close()
 | 
			
		||||
		if fi, err = f.Stat(); err != nil {
 | 
			
		||||
@@ -530,7 +525,7 @@ func (c *context) Inline(file, name string) (err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *context) contentDisposition(file, name, dispositionType string) (err error) {
 | 
			
		||||
	c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%s", dispositionType, name))
 | 
			
		||||
	c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", dispositionType, name))
 | 
			
		||||
	c.File(file)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										136
									
								
								vendor/github.com/labstack/echo/echo.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										136
									
								
								vendor/github.com/labstack/echo/echo.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -72,6 +72,7 @@ type (
 | 
			
		||||
		TLSServer        *http.Server
 | 
			
		||||
		Listener         net.Listener
 | 
			
		||||
		TLSListener      net.Listener
 | 
			
		||||
		AutoTLSManager   autocert.Manager
 | 
			
		||||
		DisableHTTP2     bool
 | 
			
		||||
		Debug            bool
 | 
			
		||||
		HideBanner       bool
 | 
			
		||||
@@ -79,22 +80,22 @@ type (
 | 
			
		||||
		Binder           Binder
 | 
			
		||||
		Validator        Validator
 | 
			
		||||
		Renderer         Renderer
 | 
			
		||||
		AutoTLSManager   autocert.Manager
 | 
			
		||||
		// Mutex            sync.RWMutex
 | 
			
		||||
		Logger Logger
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Route contains a handler and information for matching against requests.
 | 
			
		||||
	Route struct {
 | 
			
		||||
		Method  string `json:"method"`
 | 
			
		||||
		Path    string `json:"path"`
 | 
			
		||||
		Handler string `json:"handler"`
 | 
			
		||||
		Method string `json:"method"`
 | 
			
		||||
		Path   string `json:"path"`
 | 
			
		||||
		Name   string `json:"name"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// HTTPError represents an error that occurred while handling a request.
 | 
			
		||||
	HTTPError struct {
 | 
			
		||||
		Code    int
 | 
			
		||||
		Message interface{}
 | 
			
		||||
		Inner   error // Stores the error returned by an external dependency
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// MiddlewareFunc defines a function to process middleware.
 | 
			
		||||
@@ -121,7 +122,7 @@ type (
 | 
			
		||||
 | 
			
		||||
	// i is the interface for Echo and Group.
 | 
			
		||||
	i interface {
 | 
			
		||||
		GET(string, HandlerFunc, ...MiddlewareFunc)
 | 
			
		||||
		GET(string, HandlerFunc, ...MiddlewareFunc) *Route
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -212,7 +213,7 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	version = "3.1.0"
 | 
			
		||||
	version = "3.2.5"
 | 
			
		||||
	website = "https://echo.labstack.com"
 | 
			
		||||
	// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
 | 
			
		||||
	banner = `
 | 
			
		||||
@@ -282,7 +283,7 @@ func New() (e *Echo) {
 | 
			
		||||
	e.TLSServer.Handler = e
 | 
			
		||||
	e.HTTPErrorHandler = e.DefaultHTTPErrorHandler
 | 
			
		||||
	e.Binder = &DefaultBinder{}
 | 
			
		||||
	e.Logger.SetLevel(log.OFF)
 | 
			
		||||
	e.Logger.SetLevel(log.ERROR)
 | 
			
		||||
	e.stdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0)
 | 
			
		||||
	e.pool.New = func() interface{} {
 | 
			
		||||
		return e.NewContext(nil, nil)
 | 
			
		||||
@@ -319,6 +320,9 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
 | 
			
		||||
	if he, ok := err.(*HTTPError); ok {
 | 
			
		||||
		code = he.Code
 | 
			
		||||
		msg = he.Message
 | 
			
		||||
		if he.Inner != nil {
 | 
			
		||||
			msg = fmt.Sprintf("%v, %v", err, he.Inner)
 | 
			
		||||
		}
 | 
			
		||||
	} else if e.Debug {
 | 
			
		||||
		msg = err.Error()
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -328,19 +332,19 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
 | 
			
		||||
		msg = Map{"message": msg}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	e.Logger.Error(err)
 | 
			
		||||
 | 
			
		||||
	// Send response
 | 
			
		||||
	if !c.Response().Committed {
 | 
			
		||||
		if c.Request().Method == HEAD { // Issue #608
 | 
			
		||||
			if err := c.NoContent(code); err != nil {
 | 
			
		||||
				goto ERROR
 | 
			
		||||
			}
 | 
			
		||||
			err = c.NoContent(code)
 | 
			
		||||
		} else {
 | 
			
		||||
			if err := c.JSON(code, msg); err != nil {
 | 
			
		||||
				goto ERROR
 | 
			
		||||
			}
 | 
			
		||||
			err = c.JSON(code, msg)
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			e.Logger.Error(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
ERROR:
 | 
			
		||||
	e.Logger.Error(err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pre adds middleware to the chain which is run before router.
 | 
			
		||||
@@ -355,104 +359,114 @@ func (e *Echo) Use(middleware ...MiddlewareFunc) {
 | 
			
		||||
 | 
			
		||||
// CONNECT registers a new CONNECT route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(CONNECT, path, h, m...)
 | 
			
		||||
func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(CONNECT, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DELETE registers a new DELETE route for a path with matching handler in the router
 | 
			
		||||
// with optional route-level middleware.
 | 
			
		||||
func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(DELETE, path, h, m...)
 | 
			
		||||
func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(DELETE, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GET registers a new GET route for a path with matching handler in the router
 | 
			
		||||
// with optional route-level middleware.
 | 
			
		||||
func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(GET, path, h, m...)
 | 
			
		||||
func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(GET, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HEAD registers a new HEAD route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(HEAD, path, h, m...)
 | 
			
		||||
func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(HEAD, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OPTIONS registers a new OPTIONS route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(OPTIONS, path, h, m...)
 | 
			
		||||
func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(OPTIONS, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PATCH registers a new PATCH route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(PATCH, path, h, m...)
 | 
			
		||||
func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(PATCH, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// POST registers a new POST route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(POST, path, h, m...)
 | 
			
		||||
func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(POST, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PUT registers a new PUT route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(PUT, path, h, m...)
 | 
			
		||||
func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(PUT, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TRACE registers a new TRACE route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(TRACE, path, h, m...)
 | 
			
		||||
func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(TRACE, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Any registers a new route for all HTTP methods and path with matching handler
 | 
			
		||||
// in the router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
 | 
			
		||||
	routes := make([]*Route, 0)
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		e.add(m, path, handler, middleware...)
 | 
			
		||||
		routes = append(routes, e.Add(m, path, handler, middleware...))
 | 
			
		||||
	}
 | 
			
		||||
	return routes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Match registers a new route for multiple HTTP methods and path with matching
 | 
			
		||||
// handler in the router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
 | 
			
		||||
	routes := make([]*Route, 0)
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		e.add(m, path, handler, middleware...)
 | 
			
		||||
		routes = append(routes, e.Add(m, path, handler, middleware...))
 | 
			
		||||
	}
 | 
			
		||||
	return routes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Static registers a new route with path prefix to serve static files from the
 | 
			
		||||
// provided root directory.
 | 
			
		||||
func (e *Echo) Static(prefix, root string) {
 | 
			
		||||
func (e *Echo) Static(prefix, root string) *Route {
 | 
			
		||||
	if root == "" {
 | 
			
		||||
		root = "." // For security we want to restrict to CWD.
 | 
			
		||||
	}
 | 
			
		||||
	static(e, prefix, root)
 | 
			
		||||
	return static(e, prefix, root)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func static(i i, prefix, root string) {
 | 
			
		||||
func static(i i, prefix, root string) *Route {
 | 
			
		||||
	h := func(c Context) error {
 | 
			
		||||
		name := filepath.Join(root, path.Clean("/"+c.Param("*"))) // "/"+ for security
 | 
			
		||||
		p, err := PathUnescape(c.Param("*"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		name := filepath.Join(root, path.Clean("/"+p)) // "/"+ for security
 | 
			
		||||
		return c.File(name)
 | 
			
		||||
	}
 | 
			
		||||
	i.GET(prefix, h)
 | 
			
		||||
	if prefix == "/" {
 | 
			
		||||
		i.GET(prefix+"*", h)
 | 
			
		||||
	} else {
 | 
			
		||||
		i.GET(prefix+"/*", h)
 | 
			
		||||
		return i.GET(prefix+"*", h)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return i.GET(prefix+"/*", h)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// File registers a new route with path to serve a static file.
 | 
			
		||||
func (e *Echo) File(path, file string) {
 | 
			
		||||
	e.GET(path, func(c Context) error {
 | 
			
		||||
func (e *Echo) File(path, file string) *Route {
 | 
			
		||||
	return e.GET(path, func(c Context) error {
 | 
			
		||||
		return c.File(file)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
// Add registers a new route for an HTTP method and path with matching handler
 | 
			
		||||
// in the router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {
 | 
			
		||||
	name := handlerName(handler)
 | 
			
		||||
	e.router.Add(method, path, func(c Context) error {
 | 
			
		||||
		h := handler
 | 
			
		||||
@@ -463,11 +477,12 @@ func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...Middl
 | 
			
		||||
		return h(c)
 | 
			
		||||
	})
 | 
			
		||||
	r := &Route{
 | 
			
		||||
		Method:  method,
 | 
			
		||||
		Path:    path,
 | 
			
		||||
		Handler: name,
 | 
			
		||||
		Method: method,
 | 
			
		||||
		Path:   path,
 | 
			
		||||
		Name:   name,
 | 
			
		||||
	}
 | 
			
		||||
	e.router.routes[method+path] = r
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Group creates a new router group with prefix and optional group-level middleware.
 | 
			
		||||
@@ -479,12 +494,22 @@ func (e *Echo) Group(prefix string, m ...MiddlewareFunc) (g *Group) {
 | 
			
		||||
 | 
			
		||||
// URI generates a URI from handler.
 | 
			
		||||
func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string {
 | 
			
		||||
	name := handlerName(handler)
 | 
			
		||||
	return e.Reverse(name, params...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// URL is an alias for `URI` function.
 | 
			
		||||
func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
 | 
			
		||||
	return e.URI(h, params...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reverse generates an URL from route name and provided parameters.
 | 
			
		||||
func (e *Echo) Reverse(name string, params ...interface{}) string {
 | 
			
		||||
	uri := new(bytes.Buffer)
 | 
			
		||||
	ln := len(params)
 | 
			
		||||
	n := 0
 | 
			
		||||
	name := handlerName(handler)
 | 
			
		||||
	for _, r := range e.router.routes {
 | 
			
		||||
		if r.Handler == name {
 | 
			
		||||
		if r.Name == name {
 | 
			
		||||
			for i, l := 0, len(r.Path); i < l; i++ {
 | 
			
		||||
				if r.Path[i] == ':' && n < ln {
 | 
			
		||||
					for ; i < l && r.Path[i] != '/'; i++ {
 | 
			
		||||
@@ -502,11 +527,6 @@ func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string {
 | 
			
		||||
	return uri.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// URL is an alias for `URI` function.
 | 
			
		||||
func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
 | 
			
		||||
	return e.URI(h, params...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Routes returns the registered routes.
 | 
			
		||||
func (e *Echo) Routes() []*Route {
 | 
			
		||||
	routes := []*Route{}
 | 
			
		||||
@@ -653,7 +673,7 @@ func NewHTTPError(code int, message ...interface{}) *HTTPError {
 | 
			
		||||
 | 
			
		||||
// Error makes it compatible with `error` interface.
 | 
			
		||||
func (he *HTTPError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("code=%d, message=%s", he.Code, he.Message)
 | 
			
		||||
	return fmt.Sprintf("code=%d, message=%v", he.Code, he.Message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WrapHandler wraps `http.Handler` into `echo.HandlerFunc`.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								vendor/github.com/labstack/echo/group.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								vendor/github.com/labstack/echo/group.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -21,66 +21,66 @@ func (g *Group) Use(middleware ...MiddlewareFunc) {
 | 
			
		||||
	// Allow all requests to reach the group as they might get dropped if router
 | 
			
		||||
	// doesn't find a match, making none of the group middleware process.
 | 
			
		||||
	g.echo.Any(path.Clean(g.prefix+"/*"), func(c Context) error {
 | 
			
		||||
		return ErrNotFound
 | 
			
		||||
		return NotFoundHandler(c)
 | 
			
		||||
	}, g.middleware...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CONNECT implements `Echo#CONNECT()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(CONNECT, path, h, m...)
 | 
			
		||||
func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(CONNECT, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DELETE implements `Echo#DELETE()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(DELETE, path, h, m...)
 | 
			
		||||
func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(DELETE, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GET implements `Echo#GET()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(GET, path, h, m...)
 | 
			
		||||
func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(GET, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HEAD implements `Echo#HEAD()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(HEAD, path, h, m...)
 | 
			
		||||
func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(HEAD, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OPTIONS implements `Echo#OPTIONS()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(OPTIONS, path, h, m...)
 | 
			
		||||
func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(OPTIONS, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PATCH implements `Echo#PATCH()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(PATCH, path, h, m...)
 | 
			
		||||
func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(PATCH, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// POST implements `Echo#POST()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(POST, path, h, m...)
 | 
			
		||||
func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(POST, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PUT implements `Echo#PUT()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(PUT, path, h, m...)
 | 
			
		||||
func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(PUT, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TRACE implements `Echo#TRACE()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(TRACE, path, h, m...)
 | 
			
		||||
func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(TRACE, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Any implements `Echo#Any()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		g.add(m, path, handler, middleware...)
 | 
			
		||||
		g.Add(m, path, handler, middleware...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Match implements `Echo#Match()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		g.add(m, path, handler, middleware...)
 | 
			
		||||
		g.Add(m, path, handler, middleware...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -102,12 +102,13 @@ func (g *Group) File(path, file string) {
 | 
			
		||||
	g.echo.File(g.prefix+path, file)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Group) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
// Add implements `Echo#Add()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {
 | 
			
		||||
	// Combine into a new slice to avoid accidentally passing the same slice for
 | 
			
		||||
	// multiple routes, which would lead to later add() calls overwriting the
 | 
			
		||||
	// middleware from earlier calls.
 | 
			
		||||
	m := []MiddlewareFunc{}
 | 
			
		||||
	m = append(m, g.middleware...)
 | 
			
		||||
	m = append(m, middleware...)
 | 
			
		||||
	g.echo.add(method, g.prefix+path, handler, m...)
 | 
			
		||||
	return g.echo.Add(method, g.prefix+path, handler, m...)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								vendor/github.com/labstack/echo/middleware/basic_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/labstack/echo/middleware/basic_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -3,6 +3,7 @@ package middleware
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
)
 | 
			
		||||
@@ -27,7 +28,7 @@ type (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	basic        = "Basic"
 | 
			
		||||
	basic        = "basic"
 | 
			
		||||
	defaultRealm = "Restricted"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -54,7 +55,7 @@ func BasicAuth(fn BasicAuthValidator) echo.MiddlewareFunc {
 | 
			
		||||
func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Validator == nil {
 | 
			
		||||
		panic("basic-auth middleware requires a validator function")
 | 
			
		||||
		panic("echo: basic-auth middleware requires a validator function")
 | 
			
		||||
	}
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultBasicAuthConfig.Skipper
 | 
			
		||||
@@ -72,7 +73,7 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
 | 
			
		||||
			auth := c.Request().Header.Get(echo.HeaderAuthorization)
 | 
			
		||||
			l := len(basic)
 | 
			
		||||
 | 
			
		||||
			if len(auth) > l+1 && auth[:l] == basic {
 | 
			
		||||
			if len(auth) > l+1 && strings.ToLower(auth[:l]) == basic {
 | 
			
		||||
				b, err := base64.StdEncoding.DecodeString(auth[l+1:])
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										112
									
								
								vendor/github.com/labstack/echo/middleware/body_dump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/labstack/echo/middleware/body_dump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	// BodyDumpConfig defines the config for BodyDump middleware.
 | 
			
		||||
	BodyDumpConfig struct {
 | 
			
		||||
		// Skipper defines a function to skip middleware.
 | 
			
		||||
		Skipper Skipper
 | 
			
		||||
 | 
			
		||||
		// Handler receives request and response payload.
 | 
			
		||||
		// Required.
 | 
			
		||||
		Handler BodyDumpHandler
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// BodyDumpHandler receives the request and response payload.
 | 
			
		||||
	BodyDumpHandler func(echo.Context, []byte, []byte)
 | 
			
		||||
 | 
			
		||||
	bodyDumpResponseWriter struct {
 | 
			
		||||
		io.Writer
 | 
			
		||||
		http.ResponseWriter
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// DefaultBodyDumpConfig is the default Gzip middleware config.
 | 
			
		||||
	DefaultBodyDumpConfig = BodyDumpConfig{
 | 
			
		||||
		Skipper: DefaultSkipper,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// BodyDump returns a BodyDump middleware.
 | 
			
		||||
//
 | 
			
		||||
// BodyLimit middleware captures the request and response payload and calls the
 | 
			
		||||
// registered handler.
 | 
			
		||||
func BodyDump(handler BodyDumpHandler) echo.MiddlewareFunc {
 | 
			
		||||
	c := DefaultBodyDumpConfig
 | 
			
		||||
	c.Handler = handler
 | 
			
		||||
	return BodyDumpWithConfig(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BodyDumpWithConfig returns a BodyDump middleware with config.
 | 
			
		||||
// See: `BodyDump()`.
 | 
			
		||||
func BodyDumpWithConfig(config BodyDumpConfig) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Handler == nil {
 | 
			
		||||
		panic("echo: body-dump middleware requires a handler function")
 | 
			
		||||
	}
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultBodyDumpConfig.Skipper
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
		return func(c echo.Context) (err error) {
 | 
			
		||||
			if config.Skipper(c) {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Request
 | 
			
		||||
			reqBody := []byte{}
 | 
			
		||||
			if c.Request().Body != nil { // Read
 | 
			
		||||
				reqBody, _ = ioutil.ReadAll(c.Request().Body)
 | 
			
		||||
			}
 | 
			
		||||
			c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) // Reset
 | 
			
		||||
 | 
			
		||||
			// Response
 | 
			
		||||
			resBody := new(bytes.Buffer)
 | 
			
		||||
			mw := io.MultiWriter(c.Response().Writer, resBody)
 | 
			
		||||
			writer := &bodyDumpResponseWriter{Writer: mw, ResponseWriter: c.Response().Writer}
 | 
			
		||||
			c.Response().Writer = writer
 | 
			
		||||
 | 
			
		||||
			if err = next(c); err != nil {
 | 
			
		||||
				c.Error(err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Callback
 | 
			
		||||
			config.Handler(c, reqBody, resBody.Bytes())
 | 
			
		||||
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *bodyDumpResponseWriter) WriteHeader(code int) {
 | 
			
		||||
	w.ResponseWriter.WriteHeader(code)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *bodyDumpResponseWriter) Write(b []byte) (int, error) {
 | 
			
		||||
	return w.Writer.Write(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *bodyDumpResponseWriter) Flush() {
 | 
			
		||||
	w.ResponseWriter.(http.Flusher).Flush()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
 | 
			
		||||
	return w.ResponseWriter.(http.Hijacker).Hijack()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *bodyDumpResponseWriter) CloseNotify() <-chan bool {
 | 
			
		||||
	return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/labstack/echo/middleware/body_limit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/labstack/echo/middleware/body_limit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -30,7 +30,7 @@ type (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// DefaultBodyLimitConfig is the default Gzip middleware config.
 | 
			
		||||
	// DefaultBodyLimitConfig is the default BodyLimit middleware config.
 | 
			
		||||
	DefaultBodyLimitConfig = BodyLimitConfig{
 | 
			
		||||
		Skipper: DefaultSkipper,
 | 
			
		||||
	}
 | 
			
		||||
@@ -60,7 +60,7 @@ func BodyLimitWithConfig(config BodyLimitConfig) echo.MiddlewareFunc {
 | 
			
		||||
 | 
			
		||||
	limit, err := bytes.Parse(config.Limit)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(fmt.Errorf("invalid body-limit=%s", config.Limit))
 | 
			
		||||
		panic(fmt.Errorf("echo: invalid body-limit=%s", config.Limit))
 | 
			
		||||
	}
 | 
			
		||||
	config.limit = limit
 | 
			
		||||
	pool := limitedReaderPool(config)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								vendor/github.com/labstack/echo/middleware/compress.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/labstack/echo/middleware/compress.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -67,7 +67,7 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
 | 
			
		||||
			res := c.Response()
 | 
			
		||||
			res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding)
 | 
			
		||||
			if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) {
 | 
			
		||||
				res.Header().Add(echo.HeaderContentEncoding, gzipScheme) // Issue #806
 | 
			
		||||
				res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
 | 
			
		||||
				rw := res.Writer
 | 
			
		||||
				w, err := gzip.NewWriterLevel(rw, config.Level)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
@@ -98,6 +98,7 @@ func (w *gzipResponseWriter) WriteHeader(code int) {
 | 
			
		||||
	if code == http.StatusNoContent { // Issue #489
 | 
			
		||||
		w.ResponseWriter.Header().Del(echo.HeaderContentEncoding)
 | 
			
		||||
	}
 | 
			
		||||
	w.Header().Del(echo.HeaderContentLength) // Issue #444
 | 
			
		||||
	w.ResponseWriter.WriteHeader(code)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								vendor/github.com/labstack/echo/middleware/jwt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/labstack/echo/middleware/jwt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,7 +1,6 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"reflect"
 | 
			
		||||
@@ -57,6 +56,12 @@ const (
 | 
			
		||||
	AlgorithmHS256 = "HS256"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Errors
 | 
			
		||||
var (
 | 
			
		||||
	ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "Missing or malformed jwt")
 | 
			
		||||
	ErrJWTInvalid = echo.NewHTTPError(http.StatusUnauthorized, "Invalid or expired jwt")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// DefaultJWTConfig is the default JWT auth middleware config.
 | 
			
		||||
	DefaultJWTConfig = JWTConfig{
 | 
			
		||||
@@ -77,7 +82,7 @@ var (
 | 
			
		||||
//
 | 
			
		||||
// See: https://jwt.io/introduction
 | 
			
		||||
// See `JWTConfig.TokenLookup`
 | 
			
		||||
func JWT(key []byte) echo.MiddlewareFunc {
 | 
			
		||||
func JWT(key interface{}) echo.MiddlewareFunc {
 | 
			
		||||
	c := DefaultJWTConfig
 | 
			
		||||
	c.SigningKey = key
 | 
			
		||||
	return JWTWithConfig(c)
 | 
			
		||||
@@ -134,14 +139,15 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
 | 
			
		||||
 | 
			
		||||
			auth, err := extractor(c)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return echo.NewHTTPError(http.StatusBadRequest, err.Error())
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			token := new(jwt.Token)
 | 
			
		||||
			// Issue #647, #656
 | 
			
		||||
			if _, ok := config.Claims.(jwt.MapClaims); ok {
 | 
			
		||||
				token, err = jwt.Parse(auth, config.keyFunc)
 | 
			
		||||
			} else {
 | 
			
		||||
				claims := reflect.ValueOf(config.Claims).Interface().(jwt.Claims)
 | 
			
		||||
				t := reflect.ValueOf(config.Claims).Type().Elem()
 | 
			
		||||
				claims := reflect.New(t).Interface().(jwt.Claims)
 | 
			
		||||
				token, err = jwt.ParseWithClaims(auth, claims, config.keyFunc)
 | 
			
		||||
			}
 | 
			
		||||
			if err == nil && token.Valid {
 | 
			
		||||
@@ -149,7 +155,11 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
 | 
			
		||||
				c.Set(config.ContextKey, token)
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
			return echo.ErrUnauthorized
 | 
			
		||||
			return &echo.HTTPError{
 | 
			
		||||
				Code:    ErrJWTInvalid.Code,
 | 
			
		||||
				Message: ErrJWTInvalid.Message,
 | 
			
		||||
				Inner:   err,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -162,7 +172,7 @@ func jwtFromHeader(header string, authScheme string) jwtExtractor {
 | 
			
		||||
		if len(auth) > l+1 && auth[:l] == authScheme {
 | 
			
		||||
			return auth[l+1:], nil
 | 
			
		||||
		}
 | 
			
		||||
		return "", errors.New("Missing or invalid jwt in the request header")
 | 
			
		||||
		return "", ErrJWTMissing
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -171,7 +181,7 @@ func jwtFromQuery(param string) jwtExtractor {
 | 
			
		||||
	return func(c echo.Context) (string, error) {
 | 
			
		||||
		token := c.QueryParam(param)
 | 
			
		||||
		if token == "" {
 | 
			
		||||
			return "", errors.New("Missing jwt in the query string")
 | 
			
		||||
			return "", ErrJWTMissing
 | 
			
		||||
		}
 | 
			
		||||
		return token, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -182,7 +192,7 @@ func jwtFromCookie(name string) jwtExtractor {
 | 
			
		||||
	return func(c echo.Context) (string, error) {
 | 
			
		||||
		cookie, err := c.Cookie(name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", errors.New("Missing jwt in the cookie")
 | 
			
		||||
			return "", ErrJWTMissing
 | 
			
		||||
		}
 | 
			
		||||
		return cookie.Value, nil
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/labstack/echo/middleware/key_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/labstack/echo/middleware/key_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -72,7 +72,7 @@ func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc {
 | 
			
		||||
		config.KeyLookup = DefaultKeyAuthConfig.KeyLookup
 | 
			
		||||
	}
 | 
			
		||||
	if config.Validator == nil {
 | 
			
		||||
		panic("key-auth middleware requires a validator function")
 | 
			
		||||
		panic("echo: key-auth middleware requires a validator function")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Initialize
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								vendor/github.com/labstack/echo/middleware/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/labstack/echo/middleware/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,7 +1,6 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
@@ -54,35 +53,38 @@ type (
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// DefaultProxyConfig is the default Proxy middleware config.
 | 
			
		||||
	DefaultProxyConfig = ProxyConfig{
 | 
			
		||||
		Skipper: DefaultSkipper,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func proxyHTTP(t *ProxyTarget) http.Handler {
 | 
			
		||||
	return httputil.NewSingleHostReverseProxy(t.URL)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		h, ok := w.(http.Hijacker)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			c.Error(errors.New("proxy raw, not a hijacker"))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		in, _, err := h.Hijack()
 | 
			
		||||
		in, _, err := c.Response().Hijack()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", r.URL, err))
 | 
			
		||||
			c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", t.URL, err))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		defer in.Close()
 | 
			
		||||
 | 
			
		||||
		out, err := net.Dial("tcp", t.URL.Host)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", r.URL, err))
 | 
			
		||||
			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", t.URL, err))
 | 
			
		||||
			c.Error(he)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		defer out.Close()
 | 
			
		||||
 | 
			
		||||
		// Write header
 | 
			
		||||
		err = r.Write(out)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request copy error=%v, url=%s", r.URL, err))
 | 
			
		||||
			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", t.URL, err))
 | 
			
		||||
			c.Error(he)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
@@ -97,7 +99,7 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
 | 
			
		||||
		go cp(in, out)
 | 
			
		||||
		err = <-errc
 | 
			
		||||
		if err != nil && err != io.EOF {
 | 
			
		||||
			c.Logger().Errorf("proxy raw, error=%v, url=%s", r.URL, err)
 | 
			
		||||
			c.Logger().Errorf("proxy raw, copy body error=%v, url=%s", t.URL, err)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@@ -118,8 +120,18 @@ func (r *RoundRobinBalancer) Next() *ProxyTarget {
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Proxy returns an HTTP/WebSocket reverse proxy middleware.
 | 
			
		||||
func Proxy(config ProxyConfig) echo.MiddlewareFunc {
 | 
			
		||||
// Proxy returns a Proxy middleware.
 | 
			
		||||
//
 | 
			
		||||
// Proxy middleware forwards the request to upstream server using a configured load balancing technique.
 | 
			
		||||
func Proxy(balancer ProxyBalancer) echo.MiddlewareFunc {
 | 
			
		||||
	c := DefaultProxyConfig
 | 
			
		||||
	c.Balancer = balancer
 | 
			
		||||
	return ProxyWithConfig(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProxyWithConfig returns a Proxy middleware with config.
 | 
			
		||||
// See: `Proxy()`
 | 
			
		||||
func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultLoggerConfig.Skipper
 | 
			
		||||
@@ -130,6 +142,10 @@ func Proxy(config ProxyConfig) echo.MiddlewareFunc {
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
		return func(c echo.Context) (err error) {
 | 
			
		||||
			if config.Skipper(c) {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			req := c.Request()
 | 
			
		||||
			res := c.Response()
 | 
			
		||||
			tgt := config.Balancer.Next()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/labstack/echo/middleware/recover.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/labstack/echo/middleware/recover.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -5,7 +5,6 @@ import (
 | 
			
		||||
	"runtime"
 | 
			
		||||
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
	"github.com/labstack/gommon/color"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
@@ -64,17 +63,14 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc {
 | 
			
		||||
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if r := recover(); r != nil {
 | 
			
		||||
					var err error
 | 
			
		||||
					switch r := r.(type) {
 | 
			
		||||
					case error:
 | 
			
		||||
						err = r
 | 
			
		||||
					default:
 | 
			
		||||
					err, ok := r.(error)
 | 
			
		||||
					if !ok {
 | 
			
		||||
						err = fmt.Errorf("%v", r)
 | 
			
		||||
					}
 | 
			
		||||
					stack := make([]byte, config.StackSize)
 | 
			
		||||
					length := runtime.Stack(stack, !config.DisableStackAll)
 | 
			
		||||
					if !config.DisablePrintStack {
 | 
			
		||||
						c.Logger().Printf("[%s] %s %s\n", color.Red("PANIC RECOVER"), err, stack[:length])
 | 
			
		||||
						c.Logger().Printf("[PANIC RECOVER] %v %s\n", err, stack[:length])
 | 
			
		||||
					}
 | 
			
		||||
					c.Error(err)
 | 
			
		||||
				}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								vendor/github.com/labstack/echo/middleware/static.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/labstack/echo/middleware/static.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -2,6 +2,7 @@ package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
@@ -66,7 +67,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
		return func(c echo.Context) error {
 | 
			
		||||
		return func(c echo.Context) (err error) {
 | 
			
		||||
			if config.Skipper(c) {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
@@ -75,17 +76,25 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 | 
			
		||||
			if strings.HasSuffix(c.Path(), "*") { // When serving from a group, e.g. `/static*`.
 | 
			
		||||
				p = c.Param("*")
 | 
			
		||||
			}
 | 
			
		||||
			p, err = echo.PathUnescape(p)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			name := filepath.Join(config.Root, path.Clean("/"+p)) // "/"+ for security
 | 
			
		||||
 | 
			
		||||
			fi, err := os.Stat(name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if os.IsNotExist(err) {
 | 
			
		||||
					if config.HTML5 && path.Ext(p) == "" {
 | 
			
		||||
						return c.File(filepath.Join(config.Root, config.Index))
 | 
			
		||||
					if err = next(c); err != nil {
 | 
			
		||||
						if he, ok := err.(*echo.HTTPError); ok {
 | 
			
		||||
							if config.HTML5 && he.Code == http.StatusNotFound {
 | 
			
		||||
								return c.File(filepath.Join(config.Root, config.Index))
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
					return next(c)
 | 
			
		||||
				}
 | 
			
		||||
				return err
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if fi.IsDir() {
 | 
			
		||||
@@ -99,7 +108,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 | 
			
		||||
					if os.IsNotExist(err) {
 | 
			
		||||
						return next(c)
 | 
			
		||||
					}
 | 
			
		||||
					return err
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return c.File(index)
 | 
			
		||||
@@ -110,20 +119,20 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func listDir(name string, res *echo.Response) error {
 | 
			
		||||
func listDir(name string, res *echo.Response) (err error) {
 | 
			
		||||
	dir, err := os.Open(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	dirs, err := dir.Readdir(-1)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create a directory index
 | 
			
		||||
	res.Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8)
 | 
			
		||||
	if _, err = fmt.Fprintf(res, "<pre>\n"); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, d := range dirs {
 | 
			
		||||
		name := d.Name()
 | 
			
		||||
@@ -133,9 +142,9 @@ func listDir(name string, res *echo.Response) error {
 | 
			
		||||
			name += "/"
 | 
			
		||||
		}
 | 
			
		||||
		if _, err = fmt.Fprintf(res, "<a href=\"%s\" style=\"color: %s;\">%s</a>\n", name, color, name); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	_, err = fmt.Fprintf(res, "</pre>\n")
 | 
			
		||||
	return err
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								vendor/github.com/labstack/echo/response.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/labstack/echo/response.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -11,11 +11,12 @@ type (
 | 
			
		||||
	// by an HTTP handler to construct an HTTP response.
 | 
			
		||||
	// See: https://golang.org/pkg/net/http/#ResponseWriter
 | 
			
		||||
	Response struct {
 | 
			
		||||
		Writer    http.ResponseWriter
 | 
			
		||||
		Status    int
 | 
			
		||||
		Size      int64
 | 
			
		||||
		Committed bool
 | 
			
		||||
		echo      *Echo
 | 
			
		||||
		echo        *Echo
 | 
			
		||||
		beforeFuncs []func()
 | 
			
		||||
		Writer      http.ResponseWriter
 | 
			
		||||
		Status      int
 | 
			
		||||
		Size        int64
 | 
			
		||||
		Committed   bool
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -34,6 +35,11 @@ func (r *Response) Header() http.Header {
 | 
			
		||||
	return r.Writer.Header()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Before registers a function which is called just before the response is written.
 | 
			
		||||
func (r *Response) Before(fn func()) {
 | 
			
		||||
	r.beforeFuncs = append(r.beforeFuncs, fn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteHeader sends an HTTP response header with status code. If WriteHeader is
 | 
			
		||||
// not called explicitly, the first call to Write will trigger an implicit
 | 
			
		||||
// WriteHeader(http.StatusOK). Thus explicit calls to WriteHeader are mainly
 | 
			
		||||
@@ -43,6 +49,9 @@ func (r *Response) WriteHeader(code int) {
 | 
			
		||||
		r.echo.Logger.Warn("response already committed")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, fn := range r.beforeFuncs {
 | 
			
		||||
		fn()
 | 
			
		||||
	}
 | 
			
		||||
	r.Status = code
 | 
			
		||||
	r.Writer.WriteHeader(code)
 | 
			
		||||
	r.Committed = true
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/labstack/echo/router.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/labstack/echo/router.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -394,7 +394,7 @@ func (r *Router) Find(method, path string, c Context) {
 | 
			
		||||
		if cn = cn.findChildByKind(akind); cn == nil {
 | 
			
		||||
			if nn != nil {
 | 
			
		||||
				cn = nn
 | 
			
		||||
				nn = nil // Next
 | 
			
		||||
				nn = cn.parent // Next (Issue #954)
 | 
			
		||||
				search = ns
 | 
			
		||||
				if nk == pkind {
 | 
			
		||||
					goto Param
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/labstack/echo/util_go17.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/labstack/echo/util_go17.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
// +build go1.7, !go1.8
 | 
			
		||||
 | 
			
		||||
package echo
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PathUnescape is wraps `url.QueryUnescape`
 | 
			
		||||
func PathUnescape(s string) (string, error) {
 | 
			
		||||
	return url.QueryUnescape(s)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/labstack/echo/util_go18.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/labstack/echo/util_go18.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
// +build go1.8
 | 
			
		||||
 | 
			
		||||
package echo
 | 
			
		||||
 | 
			
		||||
import "net/url"
 | 
			
		||||
 | 
			
		||||
// PathUnescape is wraps `url.PathUnescape`
 | 
			
		||||
func PathUnescape(s string) (string, error) {
 | 
			
		||||
	return url.PathUnescape(s)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/shazow/rateio/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/shazow/rateio/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2015 Andrey Petrov
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
							
								
								
									
										29
									
								
								vendor/github.com/shazow/rateio/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/shazow/rateio/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
/*
 | 
			
		||||
Package rateio provides an io interfaces for rate-limiting.
 | 
			
		||||
 | 
			
		||||
This can be used to apply rate limiting to any type that implements an io-style interface.
 | 
			
		||||
 | 
			
		||||
For example, we can use it to restrict the reading rate of a net.Conn:
 | 
			
		||||
 | 
			
		||||
	type limitedConn struct {
 | 
			
		||||
		net.Conn
 | 
			
		||||
		io.Reader // Our rate-limited io.Reader for net.Conn
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	func (r *limitedConn) Read(p []byte) (n int, err error) {
 | 
			
		||||
		return r.Reader.Read(p)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// ReadLimitConn returns a net.Conn whose io.Reader interface is rate-limited by limiter.
 | 
			
		||||
	func ReadLimitConn(conn net.Conn, limiter rateio.Limiter) net.Conn {
 | 
			
		||||
		return &limitedConn{
 | 
			
		||||
			Conn:   conn,
 | 
			
		||||
			Reader: rateio.NewReader(conn, limiter),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
Then we can use ReadLimitConn to wrap our existing net.Conn and continue using
 | 
			
		||||
the wrapped version in its place.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
package rateio
 | 
			
		||||
							
								
								
									
										62
									
								
								vendor/github.com/shazow/rateio/limiter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/shazow/rateio/limiter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
package rateio
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const minInt = -int(^uint(0)>>1) - 1
 | 
			
		||||
 | 
			
		||||
// The error returned when the read rate exceeds our specification.
 | 
			
		||||
var ErrRateExceeded = errors.New("Read rate exceeded.")
 | 
			
		||||
 | 
			
		||||
// Limiter is an interface for a rate limiter.
 | 
			
		||||
// There are a few example limiters included in the package, but feel free to go wild with your own.
 | 
			
		||||
type Limiter interface {
 | 
			
		||||
	// Apply this many bytes to the limiter, return ErrRateExceeded if the defined rate is exceeded.
 | 
			
		||||
	Count(int) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// simpleLimiter is a rate limiter that restricts Amount bytes in Frequency duration.
 | 
			
		||||
type simpleLimiter struct {
 | 
			
		||||
	Amount    int
 | 
			
		||||
	Frequency time.Duration
 | 
			
		||||
 | 
			
		||||
	numRead  int
 | 
			
		||||
	timeRead time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSimpleLimiter creates a Limiter that restricts a given number of bytes per frequency.
 | 
			
		||||
func NewSimpleLimiter(amount int, frequency time.Duration) Limiter {
 | 
			
		||||
	return &simpleLimiter{
 | 
			
		||||
		Amount:    amount,
 | 
			
		||||
		Frequency: frequency,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewGracefulLimiter returns a Limiter that is the same as a
 | 
			
		||||
// SimpleLimiter but adds a grace period at the start of the rate
 | 
			
		||||
// limiting where it allows unlimited bytes to be read during that
 | 
			
		||||
// period.
 | 
			
		||||
func NewGracefulLimiter(amount int, frequency time.Duration, grace time.Duration) Limiter {
 | 
			
		||||
	return &simpleLimiter{
 | 
			
		||||
		Amount:    amount,
 | 
			
		||||
		Frequency: frequency,
 | 
			
		||||
		numRead:   minInt,
 | 
			
		||||
		timeRead:  time.Now().Add(grace),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Count applies n bytes to the limiter.
 | 
			
		||||
func (limit *simpleLimiter) Count(n int) error {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	if now.After(limit.timeRead) {
 | 
			
		||||
		limit.numRead = 0
 | 
			
		||||
		limit.timeRead = now.Add(limit.Frequency)
 | 
			
		||||
	}
 | 
			
		||||
	limit.numRead += n
 | 
			
		||||
	if limit.numRead > limit.Amount {
 | 
			
		||||
		return ErrRateExceeded
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								vendor/github.com/shazow/rateio/reader.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/shazow/rateio/reader.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
package rateio
 | 
			
		||||
 | 
			
		||||
import "io"
 | 
			
		||||
 | 
			
		||||
type reader struct {
 | 
			
		||||
	io.Reader
 | 
			
		||||
	Limiter
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read reads data into p.
 | 
			
		||||
// Returns ErrRateExceeded error if our specified read is exceeded.
 | 
			
		||||
func (r *reader) Read(p []byte) (n int, err error) {
 | 
			
		||||
	n, err = r.Reader.Read(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = r.Limiter.Count(n)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewReader proxies an io.Reader but keeps track of bytes read based on our Limiter.
 | 
			
		||||
func NewReader(r io.Reader, limiter Limiter) io.Reader {
 | 
			
		||||
	return &reader{r, limiter}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								vendor/github.com/shazow/rateio/writer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/shazow/rateio/writer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
package rateio
 | 
			
		||||
 | 
			
		||||
import "io"
 | 
			
		||||
 | 
			
		||||
type writer struct {
 | 
			
		||||
	io.Writer
 | 
			
		||||
	Limiter
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Write writes the contents of p into the buffer.
 | 
			
		||||
// Returns ErrRateExceeded error if our specified read is exceeded.
 | 
			
		||||
func (w *writer) Write(p []byte) (n int, err error) {
 | 
			
		||||
	n, err = w.Writer.Write(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = w.Limiter.Count(n)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWriter proxies an io.Writer but keeps track of bytes read based on our Limiter.
 | 
			
		||||
func NewWriter(w io.Writer, limiter Limiter) io.Writer {
 | 
			
		||||
	return &writer{w, limiter}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/shazow/ssh-chat/sshd/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/shazow/ssh-chat/sshd/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2014 Andrey Petrov
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
							
								
								
									
										72
									
								
								vendor/github.com/shazow/ssh-chat/sshd/auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								vendor/github.com/shazow/ssh-chat/sshd/auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
package sshd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/ssh"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Auth is used to authenticate connections based on public keys.
 | 
			
		||||
type Auth interface {
 | 
			
		||||
	// Whether to allow connections without a public key.
 | 
			
		||||
	AllowAnonymous() bool
 | 
			
		||||
	// Given address and public key, return if the connection should be permitted.
 | 
			
		||||
	Check(net.Addr, ssh.PublicKey) (bool, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MakeAuth makes an ssh.ServerConfig which performs authentication against an Auth implementation.
 | 
			
		||||
func MakeAuth(auth Auth) *ssh.ServerConfig {
 | 
			
		||||
	config := ssh.ServerConfig{
 | 
			
		||||
		NoClientAuth: false,
 | 
			
		||||
		// Auth-related things should be constant-time to avoid timing attacks.
 | 
			
		||||
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
 | 
			
		||||
			ok, err := auth.Check(conn.RemoteAddr(), key)
 | 
			
		||||
			if !ok {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			perm := &ssh.Permissions{Extensions: map[string]string{
 | 
			
		||||
				"pubkey": string(key.Marshal()),
 | 
			
		||||
			}}
 | 
			
		||||
			return perm, nil
 | 
			
		||||
		},
 | 
			
		||||
		KeyboardInteractiveCallback: func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
 | 
			
		||||
			if !auth.AllowAnonymous() {
 | 
			
		||||
				return nil, errors.New("public key authentication required")
 | 
			
		||||
			}
 | 
			
		||||
			_, err := auth.Check(conn.RemoteAddr(), nil)
 | 
			
		||||
			return nil, err
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MakeNoAuth makes a simple ssh.ServerConfig which allows all connections.
 | 
			
		||||
// Primarily used for testing.
 | 
			
		||||
func MakeNoAuth() *ssh.ServerConfig {
 | 
			
		||||
	config := ssh.ServerConfig{
 | 
			
		||||
		NoClientAuth: false,
 | 
			
		||||
		// Auth-related things should be constant-time to avoid timing attacks.
 | 
			
		||||
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
 | 
			
		||||
			perm := &ssh.Permissions{Extensions: map[string]string{
 | 
			
		||||
				"pubkey": string(key.Marshal()),
 | 
			
		||||
			}}
 | 
			
		||||
			return perm, nil
 | 
			
		||||
		},
 | 
			
		||||
		KeyboardInteractiveCallback: func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
 | 
			
		||||
			return nil, nil
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fingerprint performs a SHA256 BASE64 fingerprint of the PublicKey, similar to OpenSSH.
 | 
			
		||||
// See: https://anongit.mindrot.org/openssh.git/commit/?id=56d1c83cdd1ac
 | 
			
		||||
func Fingerprint(k ssh.PublicKey) string {
 | 
			
		||||
	hash := sha256.Sum256(k.Marshal())
 | 
			
		||||
	return "SHA256:" + base64.StdEncoding.EncodeToString(hash[:])
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										76
									
								
								vendor/github.com/shazow/ssh-chat/sshd/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/shazow/ssh-chat/sshd/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
package sshd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/ssh"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewRandomSigner generates a random key of a desired bit length.
 | 
			
		||||
func NewRandomSigner(bits int) (ssh.Signer, error) {
 | 
			
		||||
	key, err := rsa.GenerateKey(rand.Reader, bits)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return ssh.NewSignerFromKey(key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClientConfig creates a barebones ssh.ClientConfig to be used with ssh.Dial.
 | 
			
		||||
func NewClientConfig(name string) *ssh.ClientConfig {
 | 
			
		||||
	return &ssh.ClientConfig{
 | 
			
		||||
		User: name,
 | 
			
		||||
		Auth: []ssh.AuthMethod{
 | 
			
		||||
			ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
 | 
			
		||||
				return
 | 
			
		||||
			}),
 | 
			
		||||
		},
 | 
			
		||||
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConnectShell makes a barebones SSH client session, used for testing.
 | 
			
		||||
func ConnectShell(host string, name string, handler func(r io.Reader, w io.WriteCloser) error) error {
 | 
			
		||||
	config := NewClientConfig(name)
 | 
			
		||||
	conn, err := ssh.Dial("tcp", host, config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer conn.Close()
 | 
			
		||||
 | 
			
		||||
	session, err := conn.NewSession()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer session.Close()
 | 
			
		||||
 | 
			
		||||
	in, err := session.StdinPipe()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out, err := session.StdoutPipe()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
		err = session.RequestPty("xterm", 80, 40, ssh.TerminalModes{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	*/
 | 
			
		||||
 | 
			
		||||
	err = session.Shell()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = session.SendRequest("ping", true, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return handler(out, in)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								vendor/github.com/shazow/ssh-chat/sshd/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								vendor/github.com/shazow/ssh-chat/sshd/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
package sshd
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
	signer, err := ssh.ParsePrivateKey(privateKey)
 | 
			
		||||
 | 
			
		||||
	config := MakeNoAuth()
 | 
			
		||||
	config.AddHostKey(signer)
 | 
			
		||||
 | 
			
		||||
	s, err := ListenSSH("0.0.0.0:2022", config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// Handle opening socket error
 | 
			
		||||
	}
 | 
			
		||||
	defer s.Close()
 | 
			
		||||
 | 
			
		||||
	terminals := s.ServeTerminal()
 | 
			
		||||
 | 
			
		||||
	for term := range terminals {
 | 
			
		||||
		go func() {
 | 
			
		||||
			defer term.Close()
 | 
			
		||||
			term.SetPrompt("...")
 | 
			
		||||
			term.AutoCompleteCallback = nil // ...
 | 
			
		||||
 | 
			
		||||
			for {
 | 
			
		||||
				line, err := term.ReadLine()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				term.Write(...)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
*/
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/shazow/ssh-chat/sshd/logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/shazow/ssh-chat/sshd/logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
package sshd
 | 
			
		||||
 | 
			
		||||
import "io"
 | 
			
		||||
import stdlog "log"
 | 
			
		||||
 | 
			
		||||
var logger *stdlog.Logger
 | 
			
		||||
 | 
			
		||||
func SetLogger(w io.Writer) {
 | 
			
		||||
	flags := stdlog.Flags()
 | 
			
		||||
	prefix := "[sshd] "
 | 
			
		||||
	logger = stdlog.New(w, prefix, flags)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type nullWriter struct{}
 | 
			
		||||
 | 
			
		||||
func (nullWriter) Write(data []byte) (int, error) {
 | 
			
		||||
	return len(data), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	SetLogger(nullWriter{})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								vendor/github.com/shazow/ssh-chat/sshd/net.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								vendor/github.com/shazow/ssh-chat/sshd/net.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
package sshd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net"
 | 
			
		||||
 | 
			
		||||
	"github.com/shazow/rateio"
 | 
			
		||||
	"golang.org/x/crypto/ssh"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Container for the connection and ssh-related configuration
 | 
			
		||||
type SSHListener struct {
 | 
			
		||||
	net.Listener
 | 
			
		||||
	config *ssh.ServerConfig
 | 
			
		||||
 | 
			
		||||
	RateLimit   func() rateio.Limiter
 | 
			
		||||
	HandlerFunc func(term *Terminal)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Make an SSH listener socket
 | 
			
		||||
func ListenSSH(laddr string, config *ssh.ServerConfig) (*SSHListener, error) {
 | 
			
		||||
	socket, err := net.Listen("tcp", laddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	l := SSHListener{Listener: socket, config: config}
 | 
			
		||||
	return &l, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *SSHListener) handleConn(conn net.Conn) (*Terminal, error) {
 | 
			
		||||
	if l.RateLimit != nil {
 | 
			
		||||
		// TODO: Configurable Limiter?
 | 
			
		||||
		conn = ReadLimitConn(conn, l.RateLimit())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Upgrade TCP connection to SSH connection
 | 
			
		||||
	sshConn, channels, requests, err := ssh.NewServerConn(conn, l.config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// FIXME: Disconnect if too many faulty requests? (Avoid DoS.)
 | 
			
		||||
	go ssh.DiscardRequests(requests)
 | 
			
		||||
	return NewSession(sshConn, channels)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Accept incoming connections as terminal requests and yield them
 | 
			
		||||
func (l *SSHListener) Serve() {
 | 
			
		||||
	defer l.Close()
 | 
			
		||||
	for {
 | 
			
		||||
		conn, err := l.Accept()
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Printf("Failed to accept connection: %s", err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Goroutineify to resume accepting sockets early
 | 
			
		||||
		go func() {
 | 
			
		||||
			term, err := l.handleConn(conn)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logger.Printf("[%s] Failed to handshake: %s", conn.RemoteAddr(), err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			l.HandlerFunc(term)
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										70
									
								
								vendor/github.com/shazow/ssh-chat/sshd/pty.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								vendor/github.com/shazow/ssh-chat/sshd/pty.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
			
		||||
package sshd
 | 
			
		||||
 | 
			
		||||
import "encoding/binary"
 | 
			
		||||
 | 
			
		||||
// Helpers below are borrowed from go.crypto circa 2011:
 | 
			
		||||
 | 
			
		||||
// parsePtyRequest parses the payload of the pty-req message and extracts the
 | 
			
		||||
// dimensions of the terminal. See RFC 4254, section 6.2.
 | 
			
		||||
func parsePtyRequest(s []byte) (width, height int, ok bool) {
 | 
			
		||||
	_, s, ok = parseString(s)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	width32, s, ok := parseUint32(s)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	height32, _, ok := parseUint32(s)
 | 
			
		||||
	width = int(width32)
 | 
			
		||||
	height = int(height32)
 | 
			
		||||
	if width < 1 {
 | 
			
		||||
		ok = false
 | 
			
		||||
	}
 | 
			
		||||
	if height < 1 {
 | 
			
		||||
		ok = false
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseWinchRequest(s []byte) (width, height int, ok bool) {
 | 
			
		||||
	width32, _, ok := parseUint32(s)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	height32, _, ok := parseUint32(s)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	width = int(width32)
 | 
			
		||||
	height = int(height32)
 | 
			
		||||
	if width < 1 {
 | 
			
		||||
		ok = false
 | 
			
		||||
	}
 | 
			
		||||
	if height < 1 {
 | 
			
		||||
		ok = false
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseString(in []byte) (out string, rest []byte, ok bool) {
 | 
			
		||||
	if len(in) < 4 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	length := binary.BigEndian.Uint32(in)
 | 
			
		||||
	if uint32(len(in)) < 4+length {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	out = string(in[4 : 4+length])
 | 
			
		||||
	rest = in[4+length:]
 | 
			
		||||
	ok = true
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseUint32(in []byte) (uint32, []byte, bool) {
 | 
			
		||||
	if len(in) < 4 {
 | 
			
		||||
		return 0, nil, false
 | 
			
		||||
	}
 | 
			
		||||
	return binary.BigEndian.Uint32(in), in[4:], true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
package sshd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/shazow/rateio"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type limitedConn struct {
 | 
			
		||||
	net.Conn
 | 
			
		||||
	io.Reader // Our rate-limited io.Reader for net.Conn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *limitedConn) Read(p []byte) (n int, err error) {
 | 
			
		||||
	return r.Reader.Read(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadLimitConn returns a net.Conn whose io.Reader interface is rate-limited by limiter.
 | 
			
		||||
func ReadLimitConn(conn net.Conn, limiter rateio.Limiter) net.Conn {
 | 
			
		||||
	return &limitedConn{
 | 
			
		||||
		Conn:   conn,
 | 
			
		||||
		Reader: rateio.NewReader(conn, limiter),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Count each read as 1 unless it exceeds some number of bytes.
 | 
			
		||||
type inputLimiter struct {
 | 
			
		||||
	// TODO: Could do all kinds of fancy things here, like be more forgiving of
 | 
			
		||||
	// connections that have been around for a while.
 | 
			
		||||
 | 
			
		||||
	Amount    int
 | 
			
		||||
	Frequency time.Duration
 | 
			
		||||
 | 
			
		||||
	remaining int
 | 
			
		||||
	readCap   int
 | 
			
		||||
	numRead   int
 | 
			
		||||
	timeRead  time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewInputLimiter returns a rateio.Limiter with sensible defaults for
 | 
			
		||||
// differentiating between humans typing and bots spamming.
 | 
			
		||||
func NewInputLimiter() rateio.Limiter {
 | 
			
		||||
	grace := time.Second * 3
 | 
			
		||||
	return &inputLimiter{
 | 
			
		||||
		Amount:    2 << 14, // ~16kb, should be plenty for a high typing rate/copypasta/large key handshakes.
 | 
			
		||||
		Frequency: time.Minute * 1,
 | 
			
		||||
		readCap:   128,          // Allow up to 128 bytes per read (anecdotally, 1 character = 52 bytes over ssh)
 | 
			
		||||
		numRead:   -1024 * 1024, // Start with a 1mb grace
 | 
			
		||||
		timeRead:  time.Now().Add(grace),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Count applies 1 if n<readCap, else n
 | 
			
		||||
func (limit *inputLimiter) Count(n int) error {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	if now.After(limit.timeRead) {
 | 
			
		||||
		limit.numRead = 0
 | 
			
		||||
		limit.timeRead = now.Add(limit.Frequency)
 | 
			
		||||
	}
 | 
			
		||||
	if n <= limit.readCap {
 | 
			
		||||
		limit.numRead += 1
 | 
			
		||||
	} else {
 | 
			
		||||
		limit.numRead += n
 | 
			
		||||
	}
 | 
			
		||||
	if limit.numRead > limit.Amount {
 | 
			
		||||
		return rateio.ErrRateExceeded
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										167
									
								
								vendor/github.com/shazow/ssh-chat/sshd/terminal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								vendor/github.com/shazow/ssh-chat/sshd/terminal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,167 @@
 | 
			
		||||
package sshd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/ssh"
 | 
			
		||||
	"golang.org/x/crypto/ssh/terminal"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var keepaliveInterval = time.Second * 30
 | 
			
		||||
var keepaliveRequest = "keepalive@ssh-chat"
 | 
			
		||||
 | 
			
		||||
var ErrNoSessionChannel = errors.New("no session channel")
 | 
			
		||||
var ErrNotSessionChannel = errors.New("terminal requires session channel")
 | 
			
		||||
 | 
			
		||||
// Connection is an interface with fields necessary to operate an sshd host.
 | 
			
		||||
type Connection interface {
 | 
			
		||||
	PublicKey() ssh.PublicKey
 | 
			
		||||
	RemoteAddr() net.Addr
 | 
			
		||||
	Name() string
 | 
			
		||||
	ClientVersion() []byte
 | 
			
		||||
	Close() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type sshConn struct {
 | 
			
		||||
	*ssh.ServerConn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c sshConn) PublicKey() ssh.PublicKey {
 | 
			
		||||
	if c.Permissions == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s, ok := c.Permissions.Extensions["pubkey"]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	key, err := ssh.ParsePublicKey([]byte(s))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return key
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c sshConn) Name() string {
 | 
			
		||||
	return c.User()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extending ssh/terminal to include a closer interface
 | 
			
		||||
type Terminal struct {
 | 
			
		||||
	terminal.Terminal
 | 
			
		||||
	Conn    Connection
 | 
			
		||||
	Channel ssh.Channel
 | 
			
		||||
 | 
			
		||||
	done      chan struct{}
 | 
			
		||||
	closeOnce sync.Once
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Make new terminal from a session channel
 | 
			
		||||
func NewTerminal(conn *ssh.ServerConn, ch ssh.NewChannel) (*Terminal, error) {
 | 
			
		||||
	if ch.ChannelType() != "session" {
 | 
			
		||||
		return nil, ErrNotSessionChannel
 | 
			
		||||
	}
 | 
			
		||||
	channel, requests, err := ch.Accept()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	term := Terminal{
 | 
			
		||||
		Terminal: *terminal.NewTerminal(channel, "Connecting..."),
 | 
			
		||||
		Conn:     sshConn{conn},
 | 
			
		||||
		Channel:  channel,
 | 
			
		||||
 | 
			
		||||
		done: make(chan struct{}),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go term.listen(requests)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		// Keep-Alive Ticker
 | 
			
		||||
		ticker := time.Tick(keepaliveInterval)
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case <-ticker:
 | 
			
		||||
				_, err := channel.SendRequest(keepaliveRequest, true, nil)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					// Connection is gone
 | 
			
		||||
					logger.Printf("[%s] Keepalive failed, closing terminal: %s", term.Conn.RemoteAddr(), err)
 | 
			
		||||
					term.Close()
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			case <-term.done:
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return &term, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Find session channel and make a Terminal from it
 | 
			
		||||
func NewSession(conn *ssh.ServerConn, channels <-chan ssh.NewChannel) (*Terminal, error) {
 | 
			
		||||
	// Make a terminal from the first session found
 | 
			
		||||
	for ch := range channels {
 | 
			
		||||
		if t := ch.ChannelType(); t != "session" {
 | 
			
		||||
			logger.Printf("[%s] Ignored channel type: %s", conn.RemoteAddr(), t)
 | 
			
		||||
			ch.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return NewTerminal(conn, ch)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, ErrNoSessionChannel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close terminal and ssh connection
 | 
			
		||||
func (t *Terminal) Close() error {
 | 
			
		||||
	var err error
 | 
			
		||||
	t.closeOnce.Do(func() {
 | 
			
		||||
		close(t.done)
 | 
			
		||||
		t.Channel.Close()
 | 
			
		||||
		err = t.Conn.Close()
 | 
			
		||||
	})
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Negotiate terminal type and settings
 | 
			
		||||
func (t *Terminal) listen(requests <-chan *ssh.Request) {
 | 
			
		||||
	hasShell := false
 | 
			
		||||
 | 
			
		||||
	for req := range requests {
 | 
			
		||||
		var width, height int
 | 
			
		||||
		var ok bool
 | 
			
		||||
 | 
			
		||||
		switch req.Type {
 | 
			
		||||
		case "shell":
 | 
			
		||||
			if !hasShell {
 | 
			
		||||
				ok = true
 | 
			
		||||
				hasShell = true
 | 
			
		||||
			}
 | 
			
		||||
		case "pty-req":
 | 
			
		||||
			width, height, ok = parsePtyRequest(req.Payload)
 | 
			
		||||
			if ok {
 | 
			
		||||
				// TODO: Hardcode width to 100000?
 | 
			
		||||
				err := t.SetSize(width, height)
 | 
			
		||||
				ok = err == nil
 | 
			
		||||
			}
 | 
			
		||||
		case "window-change":
 | 
			
		||||
			width, height, ok = parseWinchRequest(req.Payload)
 | 
			
		||||
			if ok {
 | 
			
		||||
				// TODO: Hardcode width to 100000?
 | 
			
		||||
				err := t.SetSize(width, height)
 | 
			
		||||
				ok = err == nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if req.WantReply {
 | 
			
		||||
			req.Reply(ok, nil)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/golang.org/x/crypto/curve25519/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/golang.org/x/crypto/curve25519/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
Copyright (c) 2009 The Go Authors. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
met:
 | 
			
		||||
 | 
			
		||||
   * Redistributions of source code must retain the above copyright
 | 
			
		||||
notice, this list of conditions and the following disclaimer.
 | 
			
		||||
   * Redistributions in binary form must reproduce the above
 | 
			
		||||
copyright notice, this list of conditions and the following disclaimer
 | 
			
		||||
in the documentation and/or other materials provided with the
 | 
			
		||||
distribution.
 | 
			
		||||
   * Neither the name of Google Inc. nor the names of its
 | 
			
		||||
contributors may be used to endorse or promote products derived from
 | 
			
		||||
this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
							
								
								
									
										8
									
								
								vendor/golang.org/x/crypto/curve25519/const_amd64.h
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/golang.org/x/crypto/curve25519/const_amd64.h
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// This code was translated into a form compatible with 6a from the public
 | 
			
		||||
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
 | 
			
		||||
 | 
			
		||||
#define REDMASK51     0x0007FFFFFFFFFFFF
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/golang.org/x/crypto/curve25519/const_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/golang.org/x/crypto/curve25519/const_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// This code was translated into a form compatible with 6a from the public
 | 
			
		||||
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
 | 
			
		||||
 | 
			
		||||
// +build amd64,!gccgo,!appengine
 | 
			
		||||
 | 
			
		||||
// These constants cannot be encoded in non-MOVQ immediates.
 | 
			
		||||
// We access them directly from memory instead.
 | 
			
		||||
 | 
			
		||||
DATA ·_121666_213(SB)/8, $996687872
 | 
			
		||||
GLOBL ·_121666_213(SB), 8, $8
 | 
			
		||||
 | 
			
		||||
DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA
 | 
			
		||||
GLOBL ·_2P0(SB), 8, $8
 | 
			
		||||
 | 
			
		||||
DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE
 | 
			
		||||
GLOBL ·_2P1234(SB), 8, $8
 | 
			
		||||
							
								
								
									
										65
									
								
								vendor/golang.org/x/crypto/curve25519/cswap_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								vendor/golang.org/x/crypto/curve25519/cswap_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build amd64,!gccgo,!appengine
 | 
			
		||||
 | 
			
		||||
// func cswap(inout *[4][5]uint64, v uint64)
 | 
			
		||||
TEXT ·cswap(SB),7,$0
 | 
			
		||||
	MOVQ inout+0(FP),DI
 | 
			
		||||
	MOVQ v+8(FP),SI
 | 
			
		||||
 | 
			
		||||
	SUBQ $1, SI
 | 
			
		||||
	NOTQ SI
 | 
			
		||||
	MOVQ SI, X15
 | 
			
		||||
	PSHUFD $0x44, X15, X15
 | 
			
		||||
 | 
			
		||||
	MOVOU 0(DI), X0
 | 
			
		||||
	MOVOU 16(DI), X2
 | 
			
		||||
	MOVOU 32(DI), X4
 | 
			
		||||
	MOVOU 48(DI), X6
 | 
			
		||||
	MOVOU 64(DI), X8
 | 
			
		||||
	MOVOU 80(DI), X1
 | 
			
		||||
	MOVOU 96(DI), X3
 | 
			
		||||
	MOVOU 112(DI), X5
 | 
			
		||||
	MOVOU 128(DI), X7
 | 
			
		||||
	MOVOU 144(DI), X9
 | 
			
		||||
 | 
			
		||||
	MOVO X1, X10
 | 
			
		||||
	MOVO X3, X11
 | 
			
		||||
	MOVO X5, X12
 | 
			
		||||
	MOVO X7, X13
 | 
			
		||||
	MOVO X9, X14
 | 
			
		||||
 | 
			
		||||
	PXOR X0, X10
 | 
			
		||||
	PXOR X2, X11
 | 
			
		||||
	PXOR X4, X12
 | 
			
		||||
	PXOR X6, X13
 | 
			
		||||
	PXOR X8, X14
 | 
			
		||||
	PAND X15, X10
 | 
			
		||||
	PAND X15, X11
 | 
			
		||||
	PAND X15, X12
 | 
			
		||||
	PAND X15, X13
 | 
			
		||||
	PAND X15, X14
 | 
			
		||||
	PXOR X10, X0
 | 
			
		||||
	PXOR X10, X1
 | 
			
		||||
	PXOR X11, X2
 | 
			
		||||
	PXOR X11, X3
 | 
			
		||||
	PXOR X12, X4
 | 
			
		||||
	PXOR X12, X5
 | 
			
		||||
	PXOR X13, X6
 | 
			
		||||
	PXOR X13, X7
 | 
			
		||||
	PXOR X14, X8
 | 
			
		||||
	PXOR X14, X9
 | 
			
		||||
 | 
			
		||||
	MOVOU X0, 0(DI)
 | 
			
		||||
	MOVOU X2, 16(DI)
 | 
			
		||||
	MOVOU X4, 32(DI)
 | 
			
		||||
	MOVOU X6, 48(DI)
 | 
			
		||||
	MOVOU X8, 64(DI)
 | 
			
		||||
	MOVOU X1, 80(DI)
 | 
			
		||||
	MOVOU X3, 96(DI)
 | 
			
		||||
	MOVOU X5, 112(DI)
 | 
			
		||||
	MOVOU X7, 128(DI)
 | 
			
		||||
	MOVOU X9, 144(DI)
 | 
			
		||||
	RET
 | 
			
		||||
							
								
								
									
										834
									
								
								vendor/golang.org/x/crypto/curve25519/curve25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										834
									
								
								vendor/golang.org/x/crypto/curve25519/curve25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,834 @@
 | 
			
		||||
// Copyright 2013 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// We have an implementation in amd64 assembly so this code is only run on
 | 
			
		||||
// non-amd64 platforms. The amd64 assembly does not support gccgo.
 | 
			
		||||
// +build !amd64 gccgo appengine
 | 
			
		||||
 | 
			
		||||
package curve25519
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// This code is a port of the public domain, "ref10" implementation of
 | 
			
		||||
// curve25519 from SUPERCOP 20130419 by D. J. Bernstein.
 | 
			
		||||
 | 
			
		||||
// fieldElement represents an element of the field GF(2^255 - 19). An element
 | 
			
		||||
// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
 | 
			
		||||
// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on
 | 
			
		||||
// context.
 | 
			
		||||
type fieldElement [10]int32
 | 
			
		||||
 | 
			
		||||
func feZero(fe *fieldElement) {
 | 
			
		||||
	for i := range fe {
 | 
			
		||||
		fe[i] = 0
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func feOne(fe *fieldElement) {
 | 
			
		||||
	feZero(fe)
 | 
			
		||||
	fe[0] = 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func feAdd(dst, a, b *fieldElement) {
 | 
			
		||||
	for i := range dst {
 | 
			
		||||
		dst[i] = a[i] + b[i]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func feSub(dst, a, b *fieldElement) {
 | 
			
		||||
	for i := range dst {
 | 
			
		||||
		dst[i] = a[i] - b[i]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func feCopy(dst, src *fieldElement) {
 | 
			
		||||
	for i := range dst {
 | 
			
		||||
		dst[i] = src[i]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0.
 | 
			
		||||
//
 | 
			
		||||
// Preconditions: b in {0,1}.
 | 
			
		||||
func feCSwap(f, g *fieldElement, b int32) {
 | 
			
		||||
	b = -b
 | 
			
		||||
	for i := range f {
 | 
			
		||||
		t := b & (f[i] ^ g[i])
 | 
			
		||||
		f[i] ^= t
 | 
			
		||||
		g[i] ^= t
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// load3 reads a 24-bit, little-endian value from in.
 | 
			
		||||
func load3(in []byte) int64 {
 | 
			
		||||
	var r int64
 | 
			
		||||
	r = int64(in[0])
 | 
			
		||||
	r |= int64(in[1]) << 8
 | 
			
		||||
	r |= int64(in[2]) << 16
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// load4 reads a 32-bit, little-endian value from in.
 | 
			
		||||
func load4(in []byte) int64 {
 | 
			
		||||
	return int64(binary.LittleEndian.Uint32(in))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func feFromBytes(dst *fieldElement, src *[32]byte) {
 | 
			
		||||
	h0 := load4(src[:])
 | 
			
		||||
	h1 := load3(src[4:]) << 6
 | 
			
		||||
	h2 := load3(src[7:]) << 5
 | 
			
		||||
	h3 := load3(src[10:]) << 3
 | 
			
		||||
	h4 := load3(src[13:]) << 2
 | 
			
		||||
	h5 := load4(src[16:])
 | 
			
		||||
	h6 := load3(src[20:]) << 7
 | 
			
		||||
	h7 := load3(src[23:]) << 5
 | 
			
		||||
	h8 := load3(src[26:]) << 4
 | 
			
		||||
	h9 := load3(src[29:]) << 2
 | 
			
		||||
 | 
			
		||||
	var carry [10]int64
 | 
			
		||||
	carry[9] = (h9 + 1<<24) >> 25
 | 
			
		||||
	h0 += carry[9] * 19
 | 
			
		||||
	h9 -= carry[9] << 25
 | 
			
		||||
	carry[1] = (h1 + 1<<24) >> 25
 | 
			
		||||
	h2 += carry[1]
 | 
			
		||||
	h1 -= carry[1] << 25
 | 
			
		||||
	carry[3] = (h3 + 1<<24) >> 25
 | 
			
		||||
	h4 += carry[3]
 | 
			
		||||
	h3 -= carry[3] << 25
 | 
			
		||||
	carry[5] = (h5 + 1<<24) >> 25
 | 
			
		||||
	h6 += carry[5]
 | 
			
		||||
	h5 -= carry[5] << 25
 | 
			
		||||
	carry[7] = (h7 + 1<<24) >> 25
 | 
			
		||||
	h8 += carry[7]
 | 
			
		||||
	h7 -= carry[7] << 25
 | 
			
		||||
 | 
			
		||||
	carry[0] = (h0 + 1<<25) >> 26
 | 
			
		||||
	h1 += carry[0]
 | 
			
		||||
	h0 -= carry[0] << 26
 | 
			
		||||
	carry[2] = (h2 + 1<<25) >> 26
 | 
			
		||||
	h3 += carry[2]
 | 
			
		||||
	h2 -= carry[2] << 26
 | 
			
		||||
	carry[4] = (h4 + 1<<25) >> 26
 | 
			
		||||
	h5 += carry[4]
 | 
			
		||||
	h4 -= carry[4] << 26
 | 
			
		||||
	carry[6] = (h6 + 1<<25) >> 26
 | 
			
		||||
	h7 += carry[6]
 | 
			
		||||
	h6 -= carry[6] << 26
 | 
			
		||||
	carry[8] = (h8 + 1<<25) >> 26
 | 
			
		||||
	h9 += carry[8]
 | 
			
		||||
	h8 -= carry[8] << 26
 | 
			
		||||
 | 
			
		||||
	dst[0] = int32(h0)
 | 
			
		||||
	dst[1] = int32(h1)
 | 
			
		||||
	dst[2] = int32(h2)
 | 
			
		||||
	dst[3] = int32(h3)
 | 
			
		||||
	dst[4] = int32(h4)
 | 
			
		||||
	dst[5] = int32(h5)
 | 
			
		||||
	dst[6] = int32(h6)
 | 
			
		||||
	dst[7] = int32(h7)
 | 
			
		||||
	dst[8] = int32(h8)
 | 
			
		||||
	dst[9] = int32(h9)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// feToBytes marshals h to s.
 | 
			
		||||
// Preconditions:
 | 
			
		||||
//   |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
 | 
			
		||||
//
 | 
			
		||||
// Write p=2^255-19; q=floor(h/p).
 | 
			
		||||
// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
 | 
			
		||||
//
 | 
			
		||||
// Proof:
 | 
			
		||||
//   Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
 | 
			
		||||
//   Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4.
 | 
			
		||||
//
 | 
			
		||||
//   Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
 | 
			
		||||
//   Then 0<y<1.
 | 
			
		||||
//
 | 
			
		||||
//   Write r=h-pq.
 | 
			
		||||
//   Have 0<=r<=p-1=2^255-20.
 | 
			
		||||
//   Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
 | 
			
		||||
//
 | 
			
		||||
//   Write x=r+19(2^-255)r+y.
 | 
			
		||||
//   Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
 | 
			
		||||
//
 | 
			
		||||
//   Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
 | 
			
		||||
//   so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
 | 
			
		||||
func feToBytes(s *[32]byte, h *fieldElement) {
 | 
			
		||||
	var carry [10]int32
 | 
			
		||||
 | 
			
		||||
	q := (19*h[9] + (1 << 24)) >> 25
 | 
			
		||||
	q = (h[0] + q) >> 26
 | 
			
		||||
	q = (h[1] + q) >> 25
 | 
			
		||||
	q = (h[2] + q) >> 26
 | 
			
		||||
	q = (h[3] + q) >> 25
 | 
			
		||||
	q = (h[4] + q) >> 26
 | 
			
		||||
	q = (h[5] + q) >> 25
 | 
			
		||||
	q = (h[6] + q) >> 26
 | 
			
		||||
	q = (h[7] + q) >> 25
 | 
			
		||||
	q = (h[8] + q) >> 26
 | 
			
		||||
	q = (h[9] + q) >> 25
 | 
			
		||||
 | 
			
		||||
	// Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20.
 | 
			
		||||
	h[0] += 19 * q
 | 
			
		||||
	// Goal: Output h-2^255 q, which is between 0 and 2^255-20.
 | 
			
		||||
 | 
			
		||||
	carry[0] = h[0] >> 26
 | 
			
		||||
	h[1] += carry[0]
 | 
			
		||||
	h[0] -= carry[0] << 26
 | 
			
		||||
	carry[1] = h[1] >> 25
 | 
			
		||||
	h[2] += carry[1]
 | 
			
		||||
	h[1] -= carry[1] << 25
 | 
			
		||||
	carry[2] = h[2] >> 26
 | 
			
		||||
	h[3] += carry[2]
 | 
			
		||||
	h[2] -= carry[2] << 26
 | 
			
		||||
	carry[3] = h[3] >> 25
 | 
			
		||||
	h[4] += carry[3]
 | 
			
		||||
	h[3] -= carry[3] << 25
 | 
			
		||||
	carry[4] = h[4] >> 26
 | 
			
		||||
	h[5] += carry[4]
 | 
			
		||||
	h[4] -= carry[4] << 26
 | 
			
		||||
	carry[5] = h[5] >> 25
 | 
			
		||||
	h[6] += carry[5]
 | 
			
		||||
	h[5] -= carry[5] << 25
 | 
			
		||||
	carry[6] = h[6] >> 26
 | 
			
		||||
	h[7] += carry[6]
 | 
			
		||||
	h[6] -= carry[6] << 26
 | 
			
		||||
	carry[7] = h[7] >> 25
 | 
			
		||||
	h[8] += carry[7]
 | 
			
		||||
	h[7] -= carry[7] << 25
 | 
			
		||||
	carry[8] = h[8] >> 26
 | 
			
		||||
	h[9] += carry[8]
 | 
			
		||||
	h[8] -= carry[8] << 26
 | 
			
		||||
	carry[9] = h[9] >> 25
 | 
			
		||||
	h[9] -= carry[9] << 25
 | 
			
		||||
	// h10 = carry9
 | 
			
		||||
 | 
			
		||||
	// Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
 | 
			
		||||
	// Have h[0]+...+2^230 h[9] between 0 and 2^255-1;
 | 
			
		||||
	// evidently 2^255 h10-2^255 q = 0.
 | 
			
		||||
	// Goal: Output h[0]+...+2^230 h[9].
 | 
			
		||||
 | 
			
		||||
	s[0] = byte(h[0] >> 0)
 | 
			
		||||
	s[1] = byte(h[0] >> 8)
 | 
			
		||||
	s[2] = byte(h[0] >> 16)
 | 
			
		||||
	s[3] = byte((h[0] >> 24) | (h[1] << 2))
 | 
			
		||||
	s[4] = byte(h[1] >> 6)
 | 
			
		||||
	s[5] = byte(h[1] >> 14)
 | 
			
		||||
	s[6] = byte((h[1] >> 22) | (h[2] << 3))
 | 
			
		||||
	s[7] = byte(h[2] >> 5)
 | 
			
		||||
	s[8] = byte(h[2] >> 13)
 | 
			
		||||
	s[9] = byte((h[2] >> 21) | (h[3] << 5))
 | 
			
		||||
	s[10] = byte(h[3] >> 3)
 | 
			
		||||
	s[11] = byte(h[3] >> 11)
 | 
			
		||||
	s[12] = byte((h[3] >> 19) | (h[4] << 6))
 | 
			
		||||
	s[13] = byte(h[4] >> 2)
 | 
			
		||||
	s[14] = byte(h[4] >> 10)
 | 
			
		||||
	s[15] = byte(h[4] >> 18)
 | 
			
		||||
	s[16] = byte(h[5] >> 0)
 | 
			
		||||
	s[17] = byte(h[5] >> 8)
 | 
			
		||||
	s[18] = byte(h[5] >> 16)
 | 
			
		||||
	s[19] = byte((h[5] >> 24) | (h[6] << 1))
 | 
			
		||||
	s[20] = byte(h[6] >> 7)
 | 
			
		||||
	s[21] = byte(h[6] >> 15)
 | 
			
		||||
	s[22] = byte((h[6] >> 23) | (h[7] << 3))
 | 
			
		||||
	s[23] = byte(h[7] >> 5)
 | 
			
		||||
	s[24] = byte(h[7] >> 13)
 | 
			
		||||
	s[25] = byte((h[7] >> 21) | (h[8] << 4))
 | 
			
		||||
	s[26] = byte(h[8] >> 4)
 | 
			
		||||
	s[27] = byte(h[8] >> 12)
 | 
			
		||||
	s[28] = byte((h[8] >> 20) | (h[9] << 6))
 | 
			
		||||
	s[29] = byte(h[9] >> 2)
 | 
			
		||||
	s[30] = byte(h[9] >> 10)
 | 
			
		||||
	s[31] = byte(h[9] >> 18)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// feMul calculates h = f * g
 | 
			
		||||
// Can overlap h with f or g.
 | 
			
		||||
//
 | 
			
		||||
// Preconditions:
 | 
			
		||||
//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
 | 
			
		||||
//    |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
 | 
			
		||||
//
 | 
			
		||||
// Postconditions:
 | 
			
		||||
//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
 | 
			
		||||
//
 | 
			
		||||
// Notes on implementation strategy:
 | 
			
		||||
//
 | 
			
		||||
// Using schoolbook multiplication.
 | 
			
		||||
// Karatsuba would save a little in some cost models.
 | 
			
		||||
//
 | 
			
		||||
// Most multiplications by 2 and 19 are 32-bit precomputations;
 | 
			
		||||
// cheaper than 64-bit postcomputations.
 | 
			
		||||
//
 | 
			
		||||
// There is one remaining multiplication by 19 in the carry chain;
 | 
			
		||||
// one *19 precomputation can be merged into this,
 | 
			
		||||
// but the resulting data flow is considerably less clean.
 | 
			
		||||
//
 | 
			
		||||
// There are 12 carries below.
 | 
			
		||||
// 10 of them are 2-way parallelizable and vectorizable.
 | 
			
		||||
// Can get away with 11 carries, but then data flow is much deeper.
 | 
			
		||||
//
 | 
			
		||||
// With tighter constraints on inputs can squeeze carries into int32.
 | 
			
		||||
func feMul(h, f, g *fieldElement) {
 | 
			
		||||
	f0 := f[0]
 | 
			
		||||
	f1 := f[1]
 | 
			
		||||
	f2 := f[2]
 | 
			
		||||
	f3 := f[3]
 | 
			
		||||
	f4 := f[4]
 | 
			
		||||
	f5 := f[5]
 | 
			
		||||
	f6 := f[6]
 | 
			
		||||
	f7 := f[7]
 | 
			
		||||
	f8 := f[8]
 | 
			
		||||
	f9 := f[9]
 | 
			
		||||
	g0 := g[0]
 | 
			
		||||
	g1 := g[1]
 | 
			
		||||
	g2 := g[2]
 | 
			
		||||
	g3 := g[3]
 | 
			
		||||
	g4 := g[4]
 | 
			
		||||
	g5 := g[5]
 | 
			
		||||
	g6 := g[6]
 | 
			
		||||
	g7 := g[7]
 | 
			
		||||
	g8 := g[8]
 | 
			
		||||
	g9 := g[9]
 | 
			
		||||
	g1_19 := 19 * g1 // 1.4*2^29
 | 
			
		||||
	g2_19 := 19 * g2 // 1.4*2^30; still ok
 | 
			
		||||
	g3_19 := 19 * g3
 | 
			
		||||
	g4_19 := 19 * g4
 | 
			
		||||
	g5_19 := 19 * g5
 | 
			
		||||
	g6_19 := 19 * g6
 | 
			
		||||
	g7_19 := 19 * g7
 | 
			
		||||
	g8_19 := 19 * g8
 | 
			
		||||
	g9_19 := 19 * g9
 | 
			
		||||
	f1_2 := 2 * f1
 | 
			
		||||
	f3_2 := 2 * f3
 | 
			
		||||
	f5_2 := 2 * f5
 | 
			
		||||
	f7_2 := 2 * f7
 | 
			
		||||
	f9_2 := 2 * f9
 | 
			
		||||
	f0g0 := int64(f0) * int64(g0)
 | 
			
		||||
	f0g1 := int64(f0) * int64(g1)
 | 
			
		||||
	f0g2 := int64(f0) * int64(g2)
 | 
			
		||||
	f0g3 := int64(f0) * int64(g3)
 | 
			
		||||
	f0g4 := int64(f0) * int64(g4)
 | 
			
		||||
	f0g5 := int64(f0) * int64(g5)
 | 
			
		||||
	f0g6 := int64(f0) * int64(g6)
 | 
			
		||||
	f0g7 := int64(f0) * int64(g7)
 | 
			
		||||
	f0g8 := int64(f0) * int64(g8)
 | 
			
		||||
	f0g9 := int64(f0) * int64(g9)
 | 
			
		||||
	f1g0 := int64(f1) * int64(g0)
 | 
			
		||||
	f1g1_2 := int64(f1_2) * int64(g1)
 | 
			
		||||
	f1g2 := int64(f1) * int64(g2)
 | 
			
		||||
	f1g3_2 := int64(f1_2) * int64(g3)
 | 
			
		||||
	f1g4 := int64(f1) * int64(g4)
 | 
			
		||||
	f1g5_2 := int64(f1_2) * int64(g5)
 | 
			
		||||
	f1g6 := int64(f1) * int64(g6)
 | 
			
		||||
	f1g7_2 := int64(f1_2) * int64(g7)
 | 
			
		||||
	f1g8 := int64(f1) * int64(g8)
 | 
			
		||||
	f1g9_38 := int64(f1_2) * int64(g9_19)
 | 
			
		||||
	f2g0 := int64(f2) * int64(g0)
 | 
			
		||||
	f2g1 := int64(f2) * int64(g1)
 | 
			
		||||
	f2g2 := int64(f2) * int64(g2)
 | 
			
		||||
	f2g3 := int64(f2) * int64(g3)
 | 
			
		||||
	f2g4 := int64(f2) * int64(g4)
 | 
			
		||||
	f2g5 := int64(f2) * int64(g5)
 | 
			
		||||
	f2g6 := int64(f2) * int64(g6)
 | 
			
		||||
	f2g7 := int64(f2) * int64(g7)
 | 
			
		||||
	f2g8_19 := int64(f2) * int64(g8_19)
 | 
			
		||||
	f2g9_19 := int64(f2) * int64(g9_19)
 | 
			
		||||
	f3g0 := int64(f3) * int64(g0)
 | 
			
		||||
	f3g1_2 := int64(f3_2) * int64(g1)
 | 
			
		||||
	f3g2 := int64(f3) * int64(g2)
 | 
			
		||||
	f3g3_2 := int64(f3_2) * int64(g3)
 | 
			
		||||
	f3g4 := int64(f3) * int64(g4)
 | 
			
		||||
	f3g5_2 := int64(f3_2) * int64(g5)
 | 
			
		||||
	f3g6 := int64(f3) * int64(g6)
 | 
			
		||||
	f3g7_38 := int64(f3_2) * int64(g7_19)
 | 
			
		||||
	f3g8_19 := int64(f3) * int64(g8_19)
 | 
			
		||||
	f3g9_38 := int64(f3_2) * int64(g9_19)
 | 
			
		||||
	f4g0 := int64(f4) * int64(g0)
 | 
			
		||||
	f4g1 := int64(f4) * int64(g1)
 | 
			
		||||
	f4g2 := int64(f4) * int64(g2)
 | 
			
		||||
	f4g3 := int64(f4) * int64(g3)
 | 
			
		||||
	f4g4 := int64(f4) * int64(g4)
 | 
			
		||||
	f4g5 := int64(f4) * int64(g5)
 | 
			
		||||
	f4g6_19 := int64(f4) * int64(g6_19)
 | 
			
		||||
	f4g7_19 := int64(f4) * int64(g7_19)
 | 
			
		||||
	f4g8_19 := int64(f4) * int64(g8_19)
 | 
			
		||||
	f4g9_19 := int64(f4) * int64(g9_19)
 | 
			
		||||
	f5g0 := int64(f5) * int64(g0)
 | 
			
		||||
	f5g1_2 := int64(f5_2) * int64(g1)
 | 
			
		||||
	f5g2 := int64(f5) * int64(g2)
 | 
			
		||||
	f5g3_2 := int64(f5_2) * int64(g3)
 | 
			
		||||
	f5g4 := int64(f5) * int64(g4)
 | 
			
		||||
	f5g5_38 := int64(f5_2) * int64(g5_19)
 | 
			
		||||
	f5g6_19 := int64(f5) * int64(g6_19)
 | 
			
		||||
	f5g7_38 := int64(f5_2) * int64(g7_19)
 | 
			
		||||
	f5g8_19 := int64(f5) * int64(g8_19)
 | 
			
		||||
	f5g9_38 := int64(f5_2) * int64(g9_19)
 | 
			
		||||
	f6g0 := int64(f6) * int64(g0)
 | 
			
		||||
	f6g1 := int64(f6) * int64(g1)
 | 
			
		||||
	f6g2 := int64(f6) * int64(g2)
 | 
			
		||||
	f6g3 := int64(f6) * int64(g3)
 | 
			
		||||
	f6g4_19 := int64(f6) * int64(g4_19)
 | 
			
		||||
	f6g5_19 := int64(f6) * int64(g5_19)
 | 
			
		||||
	f6g6_19 := int64(f6) * int64(g6_19)
 | 
			
		||||
	f6g7_19 := int64(f6) * int64(g7_19)
 | 
			
		||||
	f6g8_19 := int64(f6) * int64(g8_19)
 | 
			
		||||
	f6g9_19 := int64(f6) * int64(g9_19)
 | 
			
		||||
	f7g0 := int64(f7) * int64(g0)
 | 
			
		||||
	f7g1_2 := int64(f7_2) * int64(g1)
 | 
			
		||||
	f7g2 := int64(f7) * int64(g2)
 | 
			
		||||
	f7g3_38 := int64(f7_2) * int64(g3_19)
 | 
			
		||||
	f7g4_19 := int64(f7) * int64(g4_19)
 | 
			
		||||
	f7g5_38 := int64(f7_2) * int64(g5_19)
 | 
			
		||||
	f7g6_19 := int64(f7) * int64(g6_19)
 | 
			
		||||
	f7g7_38 := int64(f7_2) * int64(g7_19)
 | 
			
		||||
	f7g8_19 := int64(f7) * int64(g8_19)
 | 
			
		||||
	f7g9_38 := int64(f7_2) * int64(g9_19)
 | 
			
		||||
	f8g0 := int64(f8) * int64(g0)
 | 
			
		||||
	f8g1 := int64(f8) * int64(g1)
 | 
			
		||||
	f8g2_19 := int64(f8) * int64(g2_19)
 | 
			
		||||
	f8g3_19 := int64(f8) * int64(g3_19)
 | 
			
		||||
	f8g4_19 := int64(f8) * int64(g4_19)
 | 
			
		||||
	f8g5_19 := int64(f8) * int64(g5_19)
 | 
			
		||||
	f8g6_19 := int64(f8) * int64(g6_19)
 | 
			
		||||
	f8g7_19 := int64(f8) * int64(g7_19)
 | 
			
		||||
	f8g8_19 := int64(f8) * int64(g8_19)
 | 
			
		||||
	f8g9_19 := int64(f8) * int64(g9_19)
 | 
			
		||||
	f9g0 := int64(f9) * int64(g0)
 | 
			
		||||
	f9g1_38 := int64(f9_2) * int64(g1_19)
 | 
			
		||||
	f9g2_19 := int64(f9) * int64(g2_19)
 | 
			
		||||
	f9g3_38 := int64(f9_2) * int64(g3_19)
 | 
			
		||||
	f9g4_19 := int64(f9) * int64(g4_19)
 | 
			
		||||
	f9g5_38 := int64(f9_2) * int64(g5_19)
 | 
			
		||||
	f9g6_19 := int64(f9) * int64(g6_19)
 | 
			
		||||
	f9g7_38 := int64(f9_2) * int64(g7_19)
 | 
			
		||||
	f9g8_19 := int64(f9) * int64(g8_19)
 | 
			
		||||
	f9g9_38 := int64(f9_2) * int64(g9_19)
 | 
			
		||||
	h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38
 | 
			
		||||
	h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19
 | 
			
		||||
	h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38
 | 
			
		||||
	h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19
 | 
			
		||||
	h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38
 | 
			
		||||
	h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19
 | 
			
		||||
	h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38
 | 
			
		||||
	h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19
 | 
			
		||||
	h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38
 | 
			
		||||
	h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0
 | 
			
		||||
	var carry [10]int64
 | 
			
		||||
 | 
			
		||||
	// |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38))
 | 
			
		||||
	//   i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8
 | 
			
		||||
	// |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19))
 | 
			
		||||
	//   i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9
 | 
			
		||||
 | 
			
		||||
	carry[0] = (h0 + (1 << 25)) >> 26
 | 
			
		||||
	h1 += carry[0]
 | 
			
		||||
	h0 -= carry[0] << 26
 | 
			
		||||
	carry[4] = (h4 + (1 << 25)) >> 26
 | 
			
		||||
	h5 += carry[4]
 | 
			
		||||
	h4 -= carry[4] << 26
 | 
			
		||||
	// |h0| <= 2^25
 | 
			
		||||
	// |h4| <= 2^25
 | 
			
		||||
	// |h1| <= 1.51*2^58
 | 
			
		||||
	// |h5| <= 1.51*2^58
 | 
			
		||||
 | 
			
		||||
	carry[1] = (h1 + (1 << 24)) >> 25
 | 
			
		||||
	h2 += carry[1]
 | 
			
		||||
	h1 -= carry[1] << 25
 | 
			
		||||
	carry[5] = (h5 + (1 << 24)) >> 25
 | 
			
		||||
	h6 += carry[5]
 | 
			
		||||
	h5 -= carry[5] << 25
 | 
			
		||||
	// |h1| <= 2^24; from now on fits into int32
 | 
			
		||||
	// |h5| <= 2^24; from now on fits into int32
 | 
			
		||||
	// |h2| <= 1.21*2^59
 | 
			
		||||
	// |h6| <= 1.21*2^59
 | 
			
		||||
 | 
			
		||||
	carry[2] = (h2 + (1 << 25)) >> 26
 | 
			
		||||
	h3 += carry[2]
 | 
			
		||||
	h2 -= carry[2] << 26
 | 
			
		||||
	carry[6] = (h6 + (1 << 25)) >> 26
 | 
			
		||||
	h7 += carry[6]
 | 
			
		||||
	h6 -= carry[6] << 26
 | 
			
		||||
	// |h2| <= 2^25; from now on fits into int32 unchanged
 | 
			
		||||
	// |h6| <= 2^25; from now on fits into int32 unchanged
 | 
			
		||||
	// |h3| <= 1.51*2^58
 | 
			
		||||
	// |h7| <= 1.51*2^58
 | 
			
		||||
 | 
			
		||||
	carry[3] = (h3 + (1 << 24)) >> 25
 | 
			
		||||
	h4 += carry[3]
 | 
			
		||||
	h3 -= carry[3] << 25
 | 
			
		||||
	carry[7] = (h7 + (1 << 24)) >> 25
 | 
			
		||||
	h8 += carry[7]
 | 
			
		||||
	h7 -= carry[7] << 25
 | 
			
		||||
	// |h3| <= 2^24; from now on fits into int32 unchanged
 | 
			
		||||
	// |h7| <= 2^24; from now on fits into int32 unchanged
 | 
			
		||||
	// |h4| <= 1.52*2^33
 | 
			
		||||
	// |h8| <= 1.52*2^33
 | 
			
		||||
 | 
			
		||||
	carry[4] = (h4 + (1 << 25)) >> 26
 | 
			
		||||
	h5 += carry[4]
 | 
			
		||||
	h4 -= carry[4] << 26
 | 
			
		||||
	carry[8] = (h8 + (1 << 25)) >> 26
 | 
			
		||||
	h9 += carry[8]
 | 
			
		||||
	h8 -= carry[8] << 26
 | 
			
		||||
	// |h4| <= 2^25; from now on fits into int32 unchanged
 | 
			
		||||
	// |h8| <= 2^25; from now on fits into int32 unchanged
 | 
			
		||||
	// |h5| <= 1.01*2^24
 | 
			
		||||
	// |h9| <= 1.51*2^58
 | 
			
		||||
 | 
			
		||||
	carry[9] = (h9 + (1 << 24)) >> 25
 | 
			
		||||
	h0 += carry[9] * 19
 | 
			
		||||
	h9 -= carry[9] << 25
 | 
			
		||||
	// |h9| <= 2^24; from now on fits into int32 unchanged
 | 
			
		||||
	// |h0| <= 1.8*2^37
 | 
			
		||||
 | 
			
		||||
	carry[0] = (h0 + (1 << 25)) >> 26
 | 
			
		||||
	h1 += carry[0]
 | 
			
		||||
	h0 -= carry[0] << 26
 | 
			
		||||
	// |h0| <= 2^25; from now on fits into int32 unchanged
 | 
			
		||||
	// |h1| <= 1.01*2^24
 | 
			
		||||
 | 
			
		||||
	h[0] = int32(h0)
 | 
			
		||||
	h[1] = int32(h1)
 | 
			
		||||
	h[2] = int32(h2)
 | 
			
		||||
	h[3] = int32(h3)
 | 
			
		||||
	h[4] = int32(h4)
 | 
			
		||||
	h[5] = int32(h5)
 | 
			
		||||
	h[6] = int32(h6)
 | 
			
		||||
	h[7] = int32(h7)
 | 
			
		||||
	h[8] = int32(h8)
 | 
			
		||||
	h[9] = int32(h9)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// feSquare calculates h = f*f. Can overlap h with f.
 | 
			
		||||
//
 | 
			
		||||
// Preconditions:
 | 
			
		||||
//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
 | 
			
		||||
//
 | 
			
		||||
// Postconditions:
 | 
			
		||||
//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
 | 
			
		||||
func feSquare(h, f *fieldElement) {
 | 
			
		||||
	f0 := f[0]
 | 
			
		||||
	f1 := f[1]
 | 
			
		||||
	f2 := f[2]
 | 
			
		||||
	f3 := f[3]
 | 
			
		||||
	f4 := f[4]
 | 
			
		||||
	f5 := f[5]
 | 
			
		||||
	f6 := f[6]
 | 
			
		||||
	f7 := f[7]
 | 
			
		||||
	f8 := f[8]
 | 
			
		||||
	f9 := f[9]
 | 
			
		||||
	f0_2 := 2 * f0
 | 
			
		||||
	f1_2 := 2 * f1
 | 
			
		||||
	f2_2 := 2 * f2
 | 
			
		||||
	f3_2 := 2 * f3
 | 
			
		||||
	f4_2 := 2 * f4
 | 
			
		||||
	f5_2 := 2 * f5
 | 
			
		||||
	f6_2 := 2 * f6
 | 
			
		||||
	f7_2 := 2 * f7
 | 
			
		||||
	f5_38 := 38 * f5 // 1.31*2^30
 | 
			
		||||
	f6_19 := 19 * f6 // 1.31*2^30
 | 
			
		||||
	f7_38 := 38 * f7 // 1.31*2^30
 | 
			
		||||
	f8_19 := 19 * f8 // 1.31*2^30
 | 
			
		||||
	f9_38 := 38 * f9 // 1.31*2^30
 | 
			
		||||
	f0f0 := int64(f0) * int64(f0)
 | 
			
		||||
	f0f1_2 := int64(f0_2) * int64(f1)
 | 
			
		||||
	f0f2_2 := int64(f0_2) * int64(f2)
 | 
			
		||||
	f0f3_2 := int64(f0_2) * int64(f3)
 | 
			
		||||
	f0f4_2 := int64(f0_2) * int64(f4)
 | 
			
		||||
	f0f5_2 := int64(f0_2) * int64(f5)
 | 
			
		||||
	f0f6_2 := int64(f0_2) * int64(f6)
 | 
			
		||||
	f0f7_2 := int64(f0_2) * int64(f7)
 | 
			
		||||
	f0f8_2 := int64(f0_2) * int64(f8)
 | 
			
		||||
	f0f9_2 := int64(f0_2) * int64(f9)
 | 
			
		||||
	f1f1_2 := int64(f1_2) * int64(f1)
 | 
			
		||||
	f1f2_2 := int64(f1_2) * int64(f2)
 | 
			
		||||
	f1f3_4 := int64(f1_2) * int64(f3_2)
 | 
			
		||||
	f1f4_2 := int64(f1_2) * int64(f4)
 | 
			
		||||
	f1f5_4 := int64(f1_2) * int64(f5_2)
 | 
			
		||||
	f1f6_2 := int64(f1_2) * int64(f6)
 | 
			
		||||
	f1f7_4 := int64(f1_2) * int64(f7_2)
 | 
			
		||||
	f1f8_2 := int64(f1_2) * int64(f8)
 | 
			
		||||
	f1f9_76 := int64(f1_2) * int64(f9_38)
 | 
			
		||||
	f2f2 := int64(f2) * int64(f2)
 | 
			
		||||
	f2f3_2 := int64(f2_2) * int64(f3)
 | 
			
		||||
	f2f4_2 := int64(f2_2) * int64(f4)
 | 
			
		||||
	f2f5_2 := int64(f2_2) * int64(f5)
 | 
			
		||||
	f2f6_2 := int64(f2_2) * int64(f6)
 | 
			
		||||
	f2f7_2 := int64(f2_2) * int64(f7)
 | 
			
		||||
	f2f8_38 := int64(f2_2) * int64(f8_19)
 | 
			
		||||
	f2f9_38 := int64(f2) * int64(f9_38)
 | 
			
		||||
	f3f3_2 := int64(f3_2) * int64(f3)
 | 
			
		||||
	f3f4_2 := int64(f3_2) * int64(f4)
 | 
			
		||||
	f3f5_4 := int64(f3_2) * int64(f5_2)
 | 
			
		||||
	f3f6_2 := int64(f3_2) * int64(f6)
 | 
			
		||||
	f3f7_76 := int64(f3_2) * int64(f7_38)
 | 
			
		||||
	f3f8_38 := int64(f3_2) * int64(f8_19)
 | 
			
		||||
	f3f9_76 := int64(f3_2) * int64(f9_38)
 | 
			
		||||
	f4f4 := int64(f4) * int64(f4)
 | 
			
		||||
	f4f5_2 := int64(f4_2) * int64(f5)
 | 
			
		||||
	f4f6_38 := int64(f4_2) * int64(f6_19)
 | 
			
		||||
	f4f7_38 := int64(f4) * int64(f7_38)
 | 
			
		||||
	f4f8_38 := int64(f4_2) * int64(f8_19)
 | 
			
		||||
	f4f9_38 := int64(f4) * int64(f9_38)
 | 
			
		||||
	f5f5_38 := int64(f5) * int64(f5_38)
 | 
			
		||||
	f5f6_38 := int64(f5_2) * int64(f6_19)
 | 
			
		||||
	f5f7_76 := int64(f5_2) * int64(f7_38)
 | 
			
		||||
	f5f8_38 := int64(f5_2) * int64(f8_19)
 | 
			
		||||
	f5f9_76 := int64(f5_2) * int64(f9_38)
 | 
			
		||||
	f6f6_19 := int64(f6) * int64(f6_19)
 | 
			
		||||
	f6f7_38 := int64(f6) * int64(f7_38)
 | 
			
		||||
	f6f8_38 := int64(f6_2) * int64(f8_19)
 | 
			
		||||
	f6f9_38 := int64(f6) * int64(f9_38)
 | 
			
		||||
	f7f7_38 := int64(f7) * int64(f7_38)
 | 
			
		||||
	f7f8_38 := int64(f7_2) * int64(f8_19)
 | 
			
		||||
	f7f9_76 := int64(f7_2) * int64(f9_38)
 | 
			
		||||
	f8f8_19 := int64(f8) * int64(f8_19)
 | 
			
		||||
	f8f9_38 := int64(f8) * int64(f9_38)
 | 
			
		||||
	f9f9_38 := int64(f9) * int64(f9_38)
 | 
			
		||||
	h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38
 | 
			
		||||
	h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38
 | 
			
		||||
	h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19
 | 
			
		||||
	h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38
 | 
			
		||||
	h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38
 | 
			
		||||
	h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38
 | 
			
		||||
	h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19
 | 
			
		||||
	h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38
 | 
			
		||||
	h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38
 | 
			
		||||
	h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2
 | 
			
		||||
	var carry [10]int64
 | 
			
		||||
 | 
			
		||||
	carry[0] = (h0 + (1 << 25)) >> 26
 | 
			
		||||
	h1 += carry[0]
 | 
			
		||||
	h0 -= carry[0] << 26
 | 
			
		||||
	carry[4] = (h4 + (1 << 25)) >> 26
 | 
			
		||||
	h5 += carry[4]
 | 
			
		||||
	h4 -= carry[4] << 26
 | 
			
		||||
 | 
			
		||||
	carry[1] = (h1 + (1 << 24)) >> 25
 | 
			
		||||
	h2 += carry[1]
 | 
			
		||||
	h1 -= carry[1] << 25
 | 
			
		||||
	carry[5] = (h5 + (1 << 24)) >> 25
 | 
			
		||||
	h6 += carry[5]
 | 
			
		||||
	h5 -= carry[5] << 25
 | 
			
		||||
 | 
			
		||||
	carry[2] = (h2 + (1 << 25)) >> 26
 | 
			
		||||
	h3 += carry[2]
 | 
			
		||||
	h2 -= carry[2] << 26
 | 
			
		||||
	carry[6] = (h6 + (1 << 25)) >> 26
 | 
			
		||||
	h7 += carry[6]
 | 
			
		||||
	h6 -= carry[6] << 26
 | 
			
		||||
 | 
			
		||||
	carry[3] = (h3 + (1 << 24)) >> 25
 | 
			
		||||
	h4 += carry[3]
 | 
			
		||||
	h3 -= carry[3] << 25
 | 
			
		||||
	carry[7] = (h7 + (1 << 24)) >> 25
 | 
			
		||||
	h8 += carry[7]
 | 
			
		||||
	h7 -= carry[7] << 25
 | 
			
		||||
 | 
			
		||||
	carry[4] = (h4 + (1 << 25)) >> 26
 | 
			
		||||
	h5 += carry[4]
 | 
			
		||||
	h4 -= carry[4] << 26
 | 
			
		||||
	carry[8] = (h8 + (1 << 25)) >> 26
 | 
			
		||||
	h9 += carry[8]
 | 
			
		||||
	h8 -= carry[8] << 26
 | 
			
		||||
 | 
			
		||||
	carry[9] = (h9 + (1 << 24)) >> 25
 | 
			
		||||
	h0 += carry[9] * 19
 | 
			
		||||
	h9 -= carry[9] << 25
 | 
			
		||||
 | 
			
		||||
	carry[0] = (h0 + (1 << 25)) >> 26
 | 
			
		||||
	h1 += carry[0]
 | 
			
		||||
	h0 -= carry[0] << 26
 | 
			
		||||
 | 
			
		||||
	h[0] = int32(h0)
 | 
			
		||||
	h[1] = int32(h1)
 | 
			
		||||
	h[2] = int32(h2)
 | 
			
		||||
	h[3] = int32(h3)
 | 
			
		||||
	h[4] = int32(h4)
 | 
			
		||||
	h[5] = int32(h5)
 | 
			
		||||
	h[6] = int32(h6)
 | 
			
		||||
	h[7] = int32(h7)
 | 
			
		||||
	h[8] = int32(h8)
 | 
			
		||||
	h[9] = int32(h9)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// feMul121666 calculates h = f * 121666. Can overlap h with f.
 | 
			
		||||
//
 | 
			
		||||
// Preconditions:
 | 
			
		||||
//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
 | 
			
		||||
//
 | 
			
		||||
// Postconditions:
 | 
			
		||||
//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
 | 
			
		||||
func feMul121666(h, f *fieldElement) {
 | 
			
		||||
	h0 := int64(f[0]) * 121666
 | 
			
		||||
	h1 := int64(f[1]) * 121666
 | 
			
		||||
	h2 := int64(f[2]) * 121666
 | 
			
		||||
	h3 := int64(f[3]) * 121666
 | 
			
		||||
	h4 := int64(f[4]) * 121666
 | 
			
		||||
	h5 := int64(f[5]) * 121666
 | 
			
		||||
	h6 := int64(f[6]) * 121666
 | 
			
		||||
	h7 := int64(f[7]) * 121666
 | 
			
		||||
	h8 := int64(f[8]) * 121666
 | 
			
		||||
	h9 := int64(f[9]) * 121666
 | 
			
		||||
	var carry [10]int64
 | 
			
		||||
 | 
			
		||||
	carry[9] = (h9 + (1 << 24)) >> 25
 | 
			
		||||
	h0 += carry[9] * 19
 | 
			
		||||
	h9 -= carry[9] << 25
 | 
			
		||||
	carry[1] = (h1 + (1 << 24)) >> 25
 | 
			
		||||
	h2 += carry[1]
 | 
			
		||||
	h1 -= carry[1] << 25
 | 
			
		||||
	carry[3] = (h3 + (1 << 24)) >> 25
 | 
			
		||||
	h4 += carry[3]
 | 
			
		||||
	h3 -= carry[3] << 25
 | 
			
		||||
	carry[5] = (h5 + (1 << 24)) >> 25
 | 
			
		||||
	h6 += carry[5]
 | 
			
		||||
	h5 -= carry[5] << 25
 | 
			
		||||
	carry[7] = (h7 + (1 << 24)) >> 25
 | 
			
		||||
	h8 += carry[7]
 | 
			
		||||
	h7 -= carry[7] << 25
 | 
			
		||||
 | 
			
		||||
	carry[0] = (h0 + (1 << 25)) >> 26
 | 
			
		||||
	h1 += carry[0]
 | 
			
		||||
	h0 -= carry[0] << 26
 | 
			
		||||
	carry[2] = (h2 + (1 << 25)) >> 26
 | 
			
		||||
	h3 += carry[2]
 | 
			
		||||
	h2 -= carry[2] << 26
 | 
			
		||||
	carry[4] = (h4 + (1 << 25)) >> 26
 | 
			
		||||
	h5 += carry[4]
 | 
			
		||||
	h4 -= carry[4] << 26
 | 
			
		||||
	carry[6] = (h6 + (1 << 25)) >> 26
 | 
			
		||||
	h7 += carry[6]
 | 
			
		||||
	h6 -= carry[6] << 26
 | 
			
		||||
	carry[8] = (h8 + (1 << 25)) >> 26
 | 
			
		||||
	h9 += carry[8]
 | 
			
		||||
	h8 -= carry[8] << 26
 | 
			
		||||
 | 
			
		||||
	h[0] = int32(h0)
 | 
			
		||||
	h[1] = int32(h1)
 | 
			
		||||
	h[2] = int32(h2)
 | 
			
		||||
	h[3] = int32(h3)
 | 
			
		||||
	h[4] = int32(h4)
 | 
			
		||||
	h[5] = int32(h5)
 | 
			
		||||
	h[6] = int32(h6)
 | 
			
		||||
	h[7] = int32(h7)
 | 
			
		||||
	h[8] = int32(h8)
 | 
			
		||||
	h[9] = int32(h9)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// feInvert sets out = z^-1.
 | 
			
		||||
func feInvert(out, z *fieldElement) {
 | 
			
		||||
	var t0, t1, t2, t3 fieldElement
 | 
			
		||||
	var i int
 | 
			
		||||
 | 
			
		||||
	feSquare(&t0, z)
 | 
			
		||||
	for i = 1; i < 1; i++ {
 | 
			
		||||
		feSquare(&t0, &t0)
 | 
			
		||||
	}
 | 
			
		||||
	feSquare(&t1, &t0)
 | 
			
		||||
	for i = 1; i < 2; i++ {
 | 
			
		||||
		feSquare(&t1, &t1)
 | 
			
		||||
	}
 | 
			
		||||
	feMul(&t1, z, &t1)
 | 
			
		||||
	feMul(&t0, &t0, &t1)
 | 
			
		||||
	feSquare(&t2, &t0)
 | 
			
		||||
	for i = 1; i < 1; i++ {
 | 
			
		||||
		feSquare(&t2, &t2)
 | 
			
		||||
	}
 | 
			
		||||
	feMul(&t1, &t1, &t2)
 | 
			
		||||
	feSquare(&t2, &t1)
 | 
			
		||||
	for i = 1; i < 5; i++ {
 | 
			
		||||
		feSquare(&t2, &t2)
 | 
			
		||||
	}
 | 
			
		||||
	feMul(&t1, &t2, &t1)
 | 
			
		||||
	feSquare(&t2, &t1)
 | 
			
		||||
	for i = 1; i < 10; i++ {
 | 
			
		||||
		feSquare(&t2, &t2)
 | 
			
		||||
	}
 | 
			
		||||
	feMul(&t2, &t2, &t1)
 | 
			
		||||
	feSquare(&t3, &t2)
 | 
			
		||||
	for i = 1; i < 20; i++ {
 | 
			
		||||
		feSquare(&t3, &t3)
 | 
			
		||||
	}
 | 
			
		||||
	feMul(&t2, &t3, &t2)
 | 
			
		||||
	feSquare(&t2, &t2)
 | 
			
		||||
	for i = 1; i < 10; i++ {
 | 
			
		||||
		feSquare(&t2, &t2)
 | 
			
		||||
	}
 | 
			
		||||
	feMul(&t1, &t2, &t1)
 | 
			
		||||
	feSquare(&t2, &t1)
 | 
			
		||||
	for i = 1; i < 50; i++ {
 | 
			
		||||
		feSquare(&t2, &t2)
 | 
			
		||||
	}
 | 
			
		||||
	feMul(&t2, &t2, &t1)
 | 
			
		||||
	feSquare(&t3, &t2)
 | 
			
		||||
	for i = 1; i < 100; i++ {
 | 
			
		||||
		feSquare(&t3, &t3)
 | 
			
		||||
	}
 | 
			
		||||
	feMul(&t2, &t3, &t2)
 | 
			
		||||
	feSquare(&t2, &t2)
 | 
			
		||||
	for i = 1; i < 50; i++ {
 | 
			
		||||
		feSquare(&t2, &t2)
 | 
			
		||||
	}
 | 
			
		||||
	feMul(&t1, &t2, &t1)
 | 
			
		||||
	feSquare(&t1, &t1)
 | 
			
		||||
	for i = 1; i < 5; i++ {
 | 
			
		||||
		feSquare(&t1, &t1)
 | 
			
		||||
	}
 | 
			
		||||
	feMul(out, &t1, &t0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func scalarMult(out, in, base *[32]byte) {
 | 
			
		||||
	var e [32]byte
 | 
			
		||||
 | 
			
		||||
	copy(e[:], in[:])
 | 
			
		||||
	e[0] &= 248
 | 
			
		||||
	e[31] &= 127
 | 
			
		||||
	e[31] |= 64
 | 
			
		||||
 | 
			
		||||
	var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement
 | 
			
		||||
	feFromBytes(&x1, base)
 | 
			
		||||
	feOne(&x2)
 | 
			
		||||
	feCopy(&x3, &x1)
 | 
			
		||||
	feOne(&z3)
 | 
			
		||||
 | 
			
		||||
	swap := int32(0)
 | 
			
		||||
	for pos := 254; pos >= 0; pos-- {
 | 
			
		||||
		b := e[pos/8] >> uint(pos&7)
 | 
			
		||||
		b &= 1
 | 
			
		||||
		swap ^= int32(b)
 | 
			
		||||
		feCSwap(&x2, &x3, swap)
 | 
			
		||||
		feCSwap(&z2, &z3, swap)
 | 
			
		||||
		swap = int32(b)
 | 
			
		||||
 | 
			
		||||
		feSub(&tmp0, &x3, &z3)
 | 
			
		||||
		feSub(&tmp1, &x2, &z2)
 | 
			
		||||
		feAdd(&x2, &x2, &z2)
 | 
			
		||||
		feAdd(&z2, &x3, &z3)
 | 
			
		||||
		feMul(&z3, &tmp0, &x2)
 | 
			
		||||
		feMul(&z2, &z2, &tmp1)
 | 
			
		||||
		feSquare(&tmp0, &tmp1)
 | 
			
		||||
		feSquare(&tmp1, &x2)
 | 
			
		||||
		feAdd(&x3, &z3, &z2)
 | 
			
		||||
		feSub(&z2, &z3, &z2)
 | 
			
		||||
		feMul(&x2, &tmp1, &tmp0)
 | 
			
		||||
		feSub(&tmp1, &tmp1, &tmp0)
 | 
			
		||||
		feSquare(&z2, &z2)
 | 
			
		||||
		feMul121666(&z3, &tmp1)
 | 
			
		||||
		feSquare(&x3, &x3)
 | 
			
		||||
		feAdd(&tmp0, &tmp0, &z3)
 | 
			
		||||
		feMul(&z3, &x1, &z2)
 | 
			
		||||
		feMul(&z2, &tmp1, &tmp0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	feCSwap(&x2, &x3, swap)
 | 
			
		||||
	feCSwap(&z2, &z3, swap)
 | 
			
		||||
 | 
			
		||||
	feInvert(&z2, &z2)
 | 
			
		||||
	feMul(&x2, &x2, &z2)
 | 
			
		||||
	feToBytes(out, &x2)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/golang.org/x/crypto/curve25519/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/golang.org/x/crypto/curve25519/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Package curve25519 provides an implementation of scalar multiplication on
 | 
			
		||||
// the elliptic curve known as curve25519. See https://cr.yp.to/ecdh.html
 | 
			
		||||
package curve25519 // import "golang.org/x/crypto/curve25519"
 | 
			
		||||
 | 
			
		||||
// basePoint is the x coordinate of the generator of the curve.
 | 
			
		||||
var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
 | 
			
		||||
 | 
			
		||||
// ScalarMult sets dst to the product in*base where dst and base are the x
 | 
			
		||||
// coordinates of group points and all values are in little-endian form.
 | 
			
		||||
func ScalarMult(dst, in, base *[32]byte) {
 | 
			
		||||
	scalarMult(dst, in, base)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScalarBaseMult sets dst to the product in*base where dst and base are the x
 | 
			
		||||
// coordinates of group points, base is the standard generator and all values
 | 
			
		||||
// are in little-endian form.
 | 
			
		||||
func ScalarBaseMult(dst, in *[32]byte) {
 | 
			
		||||
	ScalarMult(dst, in, &basePoint)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										73
									
								
								vendor/golang.org/x/crypto/curve25519/freeze_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								vendor/golang.org/x/crypto/curve25519/freeze_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// This code was translated into a form compatible with 6a from the public
 | 
			
		||||
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
 | 
			
		||||
 | 
			
		||||
// +build amd64,!gccgo,!appengine
 | 
			
		||||
 | 
			
		||||
#include "const_amd64.h"
 | 
			
		||||
 | 
			
		||||
// func freeze(inout *[5]uint64)
 | 
			
		||||
TEXT ·freeze(SB),7,$0-8
 | 
			
		||||
	MOVQ inout+0(FP), DI
 | 
			
		||||
 | 
			
		||||
	MOVQ 0(DI),SI
 | 
			
		||||
	MOVQ 8(DI),DX
 | 
			
		||||
	MOVQ 16(DI),CX
 | 
			
		||||
	MOVQ 24(DI),R8
 | 
			
		||||
	MOVQ 32(DI),R9
 | 
			
		||||
	MOVQ $REDMASK51,AX
 | 
			
		||||
	MOVQ AX,R10
 | 
			
		||||
	SUBQ $18,R10
 | 
			
		||||
	MOVQ $3,R11
 | 
			
		||||
REDUCELOOP:
 | 
			
		||||
	MOVQ SI,R12
 | 
			
		||||
	SHRQ $51,R12
 | 
			
		||||
	ANDQ AX,SI
 | 
			
		||||
	ADDQ R12,DX
 | 
			
		||||
	MOVQ DX,R12
 | 
			
		||||
	SHRQ $51,R12
 | 
			
		||||
	ANDQ AX,DX
 | 
			
		||||
	ADDQ R12,CX
 | 
			
		||||
	MOVQ CX,R12
 | 
			
		||||
	SHRQ $51,R12
 | 
			
		||||
	ANDQ AX,CX
 | 
			
		||||
	ADDQ R12,R8
 | 
			
		||||
	MOVQ R8,R12
 | 
			
		||||
	SHRQ $51,R12
 | 
			
		||||
	ANDQ AX,R8
 | 
			
		||||
	ADDQ R12,R9
 | 
			
		||||
	MOVQ R9,R12
 | 
			
		||||
	SHRQ $51,R12
 | 
			
		||||
	ANDQ AX,R9
 | 
			
		||||
	IMUL3Q $19,R12,R12
 | 
			
		||||
	ADDQ R12,SI
 | 
			
		||||
	SUBQ $1,R11
 | 
			
		||||
	JA REDUCELOOP
 | 
			
		||||
	MOVQ $1,R12
 | 
			
		||||
	CMPQ R10,SI
 | 
			
		||||
	CMOVQLT R11,R12
 | 
			
		||||
	CMPQ AX,DX
 | 
			
		||||
	CMOVQNE R11,R12
 | 
			
		||||
	CMPQ AX,CX
 | 
			
		||||
	CMOVQNE R11,R12
 | 
			
		||||
	CMPQ AX,R8
 | 
			
		||||
	CMOVQNE R11,R12
 | 
			
		||||
	CMPQ AX,R9
 | 
			
		||||
	CMOVQNE R11,R12
 | 
			
		||||
	NEGQ R12
 | 
			
		||||
	ANDQ R12,AX
 | 
			
		||||
	ANDQ R12,R10
 | 
			
		||||
	SUBQ R10,SI
 | 
			
		||||
	SUBQ AX,DX
 | 
			
		||||
	SUBQ AX,CX
 | 
			
		||||
	SUBQ AX,R8
 | 
			
		||||
	SUBQ AX,R9
 | 
			
		||||
	MOVQ SI,0(DI)
 | 
			
		||||
	MOVQ DX,8(DI)
 | 
			
		||||
	MOVQ CX,16(DI)
 | 
			
		||||
	MOVQ R8,24(DI)
 | 
			
		||||
	MOVQ R9,32(DI)
 | 
			
		||||
	RET
 | 
			
		||||
							
								
								
									
										1377
									
								
								vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1377
									
								
								vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										240
									
								
								vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,240 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build amd64,!gccgo,!appengine
 | 
			
		||||
 | 
			
		||||
package curve25519
 | 
			
		||||
 | 
			
		||||
// These functions are implemented in the .s files. The names of the functions
 | 
			
		||||
// in the rest of the file are also taken from the SUPERCOP sources to help
 | 
			
		||||
// people following along.
 | 
			
		||||
 | 
			
		||||
//go:noescape
 | 
			
		||||
 | 
			
		||||
func cswap(inout *[5]uint64, v uint64)
 | 
			
		||||
 | 
			
		||||
//go:noescape
 | 
			
		||||
 | 
			
		||||
func ladderstep(inout *[5][5]uint64)
 | 
			
		||||
 | 
			
		||||
//go:noescape
 | 
			
		||||
 | 
			
		||||
func freeze(inout *[5]uint64)
 | 
			
		||||
 | 
			
		||||
//go:noescape
 | 
			
		||||
 | 
			
		||||
func mul(dest, a, b *[5]uint64)
 | 
			
		||||
 | 
			
		||||
//go:noescape
 | 
			
		||||
 | 
			
		||||
func square(out, in *[5]uint64)
 | 
			
		||||
 | 
			
		||||
// mladder uses a Montgomery ladder to calculate (xr/zr) *= s.
 | 
			
		||||
func mladder(xr, zr *[5]uint64, s *[32]byte) {
 | 
			
		||||
	var work [5][5]uint64
 | 
			
		||||
 | 
			
		||||
	work[0] = *xr
 | 
			
		||||
	setint(&work[1], 1)
 | 
			
		||||
	setint(&work[2], 0)
 | 
			
		||||
	work[3] = *xr
 | 
			
		||||
	setint(&work[4], 1)
 | 
			
		||||
 | 
			
		||||
	j := uint(6)
 | 
			
		||||
	var prevbit byte
 | 
			
		||||
 | 
			
		||||
	for i := 31; i >= 0; i-- {
 | 
			
		||||
		for j < 8 {
 | 
			
		||||
			bit := ((*s)[i] >> j) & 1
 | 
			
		||||
			swap := bit ^ prevbit
 | 
			
		||||
			prevbit = bit
 | 
			
		||||
			cswap(&work[1], uint64(swap))
 | 
			
		||||
			ladderstep(&work)
 | 
			
		||||
			j--
 | 
			
		||||
		}
 | 
			
		||||
		j = 7
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*xr = work[1]
 | 
			
		||||
	*zr = work[2]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func scalarMult(out, in, base *[32]byte) {
 | 
			
		||||
	var e [32]byte
 | 
			
		||||
	copy(e[:], (*in)[:])
 | 
			
		||||
	e[0] &= 248
 | 
			
		||||
	e[31] &= 127
 | 
			
		||||
	e[31] |= 64
 | 
			
		||||
 | 
			
		||||
	var t, z [5]uint64
 | 
			
		||||
	unpack(&t, base)
 | 
			
		||||
	mladder(&t, &z, &e)
 | 
			
		||||
	invert(&z, &z)
 | 
			
		||||
	mul(&t, &t, &z)
 | 
			
		||||
	pack(out, &t)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setint(r *[5]uint64, v uint64) {
 | 
			
		||||
	r[0] = v
 | 
			
		||||
	r[1] = 0
 | 
			
		||||
	r[2] = 0
 | 
			
		||||
	r[3] = 0
 | 
			
		||||
	r[4] = 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unpack sets r = x where r consists of 5, 51-bit limbs in little-endian
 | 
			
		||||
// order.
 | 
			
		||||
func unpack(r *[5]uint64, x *[32]byte) {
 | 
			
		||||
	r[0] = uint64(x[0]) |
 | 
			
		||||
		uint64(x[1])<<8 |
 | 
			
		||||
		uint64(x[2])<<16 |
 | 
			
		||||
		uint64(x[3])<<24 |
 | 
			
		||||
		uint64(x[4])<<32 |
 | 
			
		||||
		uint64(x[5])<<40 |
 | 
			
		||||
		uint64(x[6]&7)<<48
 | 
			
		||||
 | 
			
		||||
	r[1] = uint64(x[6])>>3 |
 | 
			
		||||
		uint64(x[7])<<5 |
 | 
			
		||||
		uint64(x[8])<<13 |
 | 
			
		||||
		uint64(x[9])<<21 |
 | 
			
		||||
		uint64(x[10])<<29 |
 | 
			
		||||
		uint64(x[11])<<37 |
 | 
			
		||||
		uint64(x[12]&63)<<45
 | 
			
		||||
 | 
			
		||||
	r[2] = uint64(x[12])>>6 |
 | 
			
		||||
		uint64(x[13])<<2 |
 | 
			
		||||
		uint64(x[14])<<10 |
 | 
			
		||||
		uint64(x[15])<<18 |
 | 
			
		||||
		uint64(x[16])<<26 |
 | 
			
		||||
		uint64(x[17])<<34 |
 | 
			
		||||
		uint64(x[18])<<42 |
 | 
			
		||||
		uint64(x[19]&1)<<50
 | 
			
		||||
 | 
			
		||||
	r[3] = uint64(x[19])>>1 |
 | 
			
		||||
		uint64(x[20])<<7 |
 | 
			
		||||
		uint64(x[21])<<15 |
 | 
			
		||||
		uint64(x[22])<<23 |
 | 
			
		||||
		uint64(x[23])<<31 |
 | 
			
		||||
		uint64(x[24])<<39 |
 | 
			
		||||
		uint64(x[25]&15)<<47
 | 
			
		||||
 | 
			
		||||
	r[4] = uint64(x[25])>>4 |
 | 
			
		||||
		uint64(x[26])<<4 |
 | 
			
		||||
		uint64(x[27])<<12 |
 | 
			
		||||
		uint64(x[28])<<20 |
 | 
			
		||||
		uint64(x[29])<<28 |
 | 
			
		||||
		uint64(x[30])<<36 |
 | 
			
		||||
		uint64(x[31]&127)<<44
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pack sets out = x where out is the usual, little-endian form of the 5,
 | 
			
		||||
// 51-bit limbs in x.
 | 
			
		||||
func pack(out *[32]byte, x *[5]uint64) {
 | 
			
		||||
	t := *x
 | 
			
		||||
	freeze(&t)
 | 
			
		||||
 | 
			
		||||
	out[0] = byte(t[0])
 | 
			
		||||
	out[1] = byte(t[0] >> 8)
 | 
			
		||||
	out[2] = byte(t[0] >> 16)
 | 
			
		||||
	out[3] = byte(t[0] >> 24)
 | 
			
		||||
	out[4] = byte(t[0] >> 32)
 | 
			
		||||
	out[5] = byte(t[0] >> 40)
 | 
			
		||||
	out[6] = byte(t[0] >> 48)
 | 
			
		||||
 | 
			
		||||
	out[6] ^= byte(t[1]<<3) & 0xf8
 | 
			
		||||
	out[7] = byte(t[1] >> 5)
 | 
			
		||||
	out[8] = byte(t[1] >> 13)
 | 
			
		||||
	out[9] = byte(t[1] >> 21)
 | 
			
		||||
	out[10] = byte(t[1] >> 29)
 | 
			
		||||
	out[11] = byte(t[1] >> 37)
 | 
			
		||||
	out[12] = byte(t[1] >> 45)
 | 
			
		||||
 | 
			
		||||
	out[12] ^= byte(t[2]<<6) & 0xc0
 | 
			
		||||
	out[13] = byte(t[2] >> 2)
 | 
			
		||||
	out[14] = byte(t[2] >> 10)
 | 
			
		||||
	out[15] = byte(t[2] >> 18)
 | 
			
		||||
	out[16] = byte(t[2] >> 26)
 | 
			
		||||
	out[17] = byte(t[2] >> 34)
 | 
			
		||||
	out[18] = byte(t[2] >> 42)
 | 
			
		||||
	out[19] = byte(t[2] >> 50)
 | 
			
		||||
 | 
			
		||||
	out[19] ^= byte(t[3]<<1) & 0xfe
 | 
			
		||||
	out[20] = byte(t[3] >> 7)
 | 
			
		||||
	out[21] = byte(t[3] >> 15)
 | 
			
		||||
	out[22] = byte(t[3] >> 23)
 | 
			
		||||
	out[23] = byte(t[3] >> 31)
 | 
			
		||||
	out[24] = byte(t[3] >> 39)
 | 
			
		||||
	out[25] = byte(t[3] >> 47)
 | 
			
		||||
 | 
			
		||||
	out[25] ^= byte(t[4]<<4) & 0xf0
 | 
			
		||||
	out[26] = byte(t[4] >> 4)
 | 
			
		||||
	out[27] = byte(t[4] >> 12)
 | 
			
		||||
	out[28] = byte(t[4] >> 20)
 | 
			
		||||
	out[29] = byte(t[4] >> 28)
 | 
			
		||||
	out[30] = byte(t[4] >> 36)
 | 
			
		||||
	out[31] = byte(t[4] >> 44)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// invert calculates r = x^-1 mod p using Fermat's little theorem.
 | 
			
		||||
func invert(r *[5]uint64, x *[5]uint64) {
 | 
			
		||||
	var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64
 | 
			
		||||
 | 
			
		||||
	square(&z2, x)        /* 2 */
 | 
			
		||||
	square(&t, &z2)       /* 4 */
 | 
			
		||||
	square(&t, &t)        /* 8 */
 | 
			
		||||
	mul(&z9, &t, x)       /* 9 */
 | 
			
		||||
	mul(&z11, &z9, &z2)   /* 11 */
 | 
			
		||||
	square(&t, &z11)      /* 22 */
 | 
			
		||||
	mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */
 | 
			
		||||
 | 
			
		||||
	square(&t, &z2_5_0)      /* 2^6 - 2^1 */
 | 
			
		||||
	for i := 1; i < 5; i++ { /* 2^20 - 2^10 */
 | 
			
		||||
		square(&t, &t)
 | 
			
		||||
	}
 | 
			
		||||
	mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */
 | 
			
		||||
 | 
			
		||||
	square(&t, &z2_10_0)      /* 2^11 - 2^1 */
 | 
			
		||||
	for i := 1; i < 10; i++ { /* 2^20 - 2^10 */
 | 
			
		||||
		square(&t, &t)
 | 
			
		||||
	}
 | 
			
		||||
	mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */
 | 
			
		||||
 | 
			
		||||
	square(&t, &z2_20_0)      /* 2^21 - 2^1 */
 | 
			
		||||
	for i := 1; i < 20; i++ { /* 2^40 - 2^20 */
 | 
			
		||||
		square(&t, &t)
 | 
			
		||||
	}
 | 
			
		||||
	mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */
 | 
			
		||||
 | 
			
		||||
	square(&t, &t)            /* 2^41 - 2^1 */
 | 
			
		||||
	for i := 1; i < 10; i++ { /* 2^50 - 2^10 */
 | 
			
		||||
		square(&t, &t)
 | 
			
		||||
	}
 | 
			
		||||
	mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */
 | 
			
		||||
 | 
			
		||||
	square(&t, &z2_50_0)      /* 2^51 - 2^1 */
 | 
			
		||||
	for i := 1; i < 50; i++ { /* 2^100 - 2^50 */
 | 
			
		||||
		square(&t, &t)
 | 
			
		||||
	}
 | 
			
		||||
	mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */
 | 
			
		||||
 | 
			
		||||
	square(&t, &z2_100_0)      /* 2^101 - 2^1 */
 | 
			
		||||
	for i := 1; i < 100; i++ { /* 2^200 - 2^100 */
 | 
			
		||||
		square(&t, &t)
 | 
			
		||||
	}
 | 
			
		||||
	mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */
 | 
			
		||||
 | 
			
		||||
	square(&t, &t)            /* 2^201 - 2^1 */
 | 
			
		||||
	for i := 1; i < 50; i++ { /* 2^250 - 2^50 */
 | 
			
		||||
		square(&t, &t)
 | 
			
		||||
	}
 | 
			
		||||
	mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */
 | 
			
		||||
 | 
			
		||||
	square(&t, &t) /* 2^251 - 2^1 */
 | 
			
		||||
	square(&t, &t) /* 2^252 - 2^2 */
 | 
			
		||||
	square(&t, &t) /* 2^253 - 2^3 */
 | 
			
		||||
 | 
			
		||||
	square(&t, &t) /* 2^254 - 2^4 */
 | 
			
		||||
 | 
			
		||||
	square(&t, &t)   /* 2^255 - 2^5 */
 | 
			
		||||
	mul(r, &t, &z11) /* 2^255 - 21 */
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										169
									
								
								vendor/golang.org/x/crypto/curve25519/mul_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								vendor/golang.org/x/crypto/curve25519/mul_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,169 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// This code was translated into a form compatible with 6a from the public
 | 
			
		||||
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
 | 
			
		||||
 | 
			
		||||
// +build amd64,!gccgo,!appengine
 | 
			
		||||
 | 
			
		||||
#include "const_amd64.h"
 | 
			
		||||
 | 
			
		||||
// func mul(dest, a, b *[5]uint64)
 | 
			
		||||
TEXT ·mul(SB),0,$16-24
 | 
			
		||||
	MOVQ dest+0(FP), DI
 | 
			
		||||
	MOVQ a+8(FP), SI
 | 
			
		||||
	MOVQ b+16(FP), DX
 | 
			
		||||
 | 
			
		||||
	MOVQ DX,CX
 | 
			
		||||
	MOVQ 24(SI),DX
 | 
			
		||||
	IMUL3Q $19,DX,AX
 | 
			
		||||
	MOVQ AX,0(SP)
 | 
			
		||||
	MULQ 16(CX)
 | 
			
		||||
	MOVQ AX,R8
 | 
			
		||||
	MOVQ DX,R9
 | 
			
		||||
	MOVQ 32(SI),DX
 | 
			
		||||
	IMUL3Q $19,DX,AX
 | 
			
		||||
	MOVQ AX,8(SP)
 | 
			
		||||
	MULQ 8(CX)
 | 
			
		||||
	ADDQ AX,R8
 | 
			
		||||
	ADCQ DX,R9
 | 
			
		||||
	MOVQ 0(SI),AX
 | 
			
		||||
	MULQ 0(CX)
 | 
			
		||||
	ADDQ AX,R8
 | 
			
		||||
	ADCQ DX,R9
 | 
			
		||||
	MOVQ 0(SI),AX
 | 
			
		||||
	MULQ 8(CX)
 | 
			
		||||
	MOVQ AX,R10
 | 
			
		||||
	MOVQ DX,R11
 | 
			
		||||
	MOVQ 0(SI),AX
 | 
			
		||||
	MULQ 16(CX)
 | 
			
		||||
	MOVQ AX,R12
 | 
			
		||||
	MOVQ DX,R13
 | 
			
		||||
	MOVQ 0(SI),AX
 | 
			
		||||
	MULQ 24(CX)
 | 
			
		||||
	MOVQ AX,R14
 | 
			
		||||
	MOVQ DX,R15
 | 
			
		||||
	MOVQ 0(SI),AX
 | 
			
		||||
	MULQ 32(CX)
 | 
			
		||||
	MOVQ AX,BX
 | 
			
		||||
	MOVQ DX,BP
 | 
			
		||||
	MOVQ 8(SI),AX
 | 
			
		||||
	MULQ 0(CX)
 | 
			
		||||
	ADDQ AX,R10
 | 
			
		||||
	ADCQ DX,R11
 | 
			
		||||
	MOVQ 8(SI),AX
 | 
			
		||||
	MULQ 8(CX)
 | 
			
		||||
	ADDQ AX,R12
 | 
			
		||||
	ADCQ DX,R13
 | 
			
		||||
	MOVQ 8(SI),AX
 | 
			
		||||
	MULQ 16(CX)
 | 
			
		||||
	ADDQ AX,R14
 | 
			
		||||
	ADCQ DX,R15
 | 
			
		||||
	MOVQ 8(SI),AX
 | 
			
		||||
	MULQ 24(CX)
 | 
			
		||||
	ADDQ AX,BX
 | 
			
		||||
	ADCQ DX,BP
 | 
			
		||||
	MOVQ 8(SI),DX
 | 
			
		||||
	IMUL3Q $19,DX,AX
 | 
			
		||||
	MULQ 32(CX)
 | 
			
		||||
	ADDQ AX,R8
 | 
			
		||||
	ADCQ DX,R9
 | 
			
		||||
	MOVQ 16(SI),AX
 | 
			
		||||
	MULQ 0(CX)
 | 
			
		||||
	ADDQ AX,R12
 | 
			
		||||
	ADCQ DX,R13
 | 
			
		||||
	MOVQ 16(SI),AX
 | 
			
		||||
	MULQ 8(CX)
 | 
			
		||||
	ADDQ AX,R14
 | 
			
		||||
	ADCQ DX,R15
 | 
			
		||||
	MOVQ 16(SI),AX
 | 
			
		||||
	MULQ 16(CX)
 | 
			
		||||
	ADDQ AX,BX
 | 
			
		||||
	ADCQ DX,BP
 | 
			
		||||
	MOVQ 16(SI),DX
 | 
			
		||||
	IMUL3Q $19,DX,AX
 | 
			
		||||
	MULQ 24(CX)
 | 
			
		||||
	ADDQ AX,R8
 | 
			
		||||
	ADCQ DX,R9
 | 
			
		||||
	MOVQ 16(SI),DX
 | 
			
		||||
	IMUL3Q $19,DX,AX
 | 
			
		||||
	MULQ 32(CX)
 | 
			
		||||
	ADDQ AX,R10
 | 
			
		||||
	ADCQ DX,R11
 | 
			
		||||
	MOVQ 24(SI),AX
 | 
			
		||||
	MULQ 0(CX)
 | 
			
		||||
	ADDQ AX,R14
 | 
			
		||||
	ADCQ DX,R15
 | 
			
		||||
	MOVQ 24(SI),AX
 | 
			
		||||
	MULQ 8(CX)
 | 
			
		||||
	ADDQ AX,BX
 | 
			
		||||
	ADCQ DX,BP
 | 
			
		||||
	MOVQ 0(SP),AX
 | 
			
		||||
	MULQ 24(CX)
 | 
			
		||||
	ADDQ AX,R10
 | 
			
		||||
	ADCQ DX,R11
 | 
			
		||||
	MOVQ 0(SP),AX
 | 
			
		||||
	MULQ 32(CX)
 | 
			
		||||
	ADDQ AX,R12
 | 
			
		||||
	ADCQ DX,R13
 | 
			
		||||
	MOVQ 32(SI),AX
 | 
			
		||||
	MULQ 0(CX)
 | 
			
		||||
	ADDQ AX,BX
 | 
			
		||||
	ADCQ DX,BP
 | 
			
		||||
	MOVQ 8(SP),AX
 | 
			
		||||
	MULQ 16(CX)
 | 
			
		||||
	ADDQ AX,R10
 | 
			
		||||
	ADCQ DX,R11
 | 
			
		||||
	MOVQ 8(SP),AX
 | 
			
		||||
	MULQ 24(CX)
 | 
			
		||||
	ADDQ AX,R12
 | 
			
		||||
	ADCQ DX,R13
 | 
			
		||||
	MOVQ 8(SP),AX
 | 
			
		||||
	MULQ 32(CX)
 | 
			
		||||
	ADDQ AX,R14
 | 
			
		||||
	ADCQ DX,R15
 | 
			
		||||
	MOVQ $REDMASK51,SI
 | 
			
		||||
	SHLQ $13,R9:R8
 | 
			
		||||
	ANDQ SI,R8
 | 
			
		||||
	SHLQ $13,R11:R10
 | 
			
		||||
	ANDQ SI,R10
 | 
			
		||||
	ADDQ R9,R10
 | 
			
		||||
	SHLQ $13,R13:R12
 | 
			
		||||
	ANDQ SI,R12
 | 
			
		||||
	ADDQ R11,R12
 | 
			
		||||
	SHLQ $13,R15:R14
 | 
			
		||||
	ANDQ SI,R14
 | 
			
		||||
	ADDQ R13,R14
 | 
			
		||||
	SHLQ $13,BP:BX
 | 
			
		||||
	ANDQ SI,BX
 | 
			
		||||
	ADDQ R15,BX
 | 
			
		||||
	IMUL3Q $19,BP,DX
 | 
			
		||||
	ADDQ DX,R8
 | 
			
		||||
	MOVQ R8,DX
 | 
			
		||||
	SHRQ $51,DX
 | 
			
		||||
	ADDQ R10,DX
 | 
			
		||||
	MOVQ DX,CX
 | 
			
		||||
	SHRQ $51,DX
 | 
			
		||||
	ANDQ SI,R8
 | 
			
		||||
	ADDQ R12,DX
 | 
			
		||||
	MOVQ DX,R9
 | 
			
		||||
	SHRQ $51,DX
 | 
			
		||||
	ANDQ SI,CX
 | 
			
		||||
	ADDQ R14,DX
 | 
			
		||||
	MOVQ DX,AX
 | 
			
		||||
	SHRQ $51,DX
 | 
			
		||||
	ANDQ SI,R9
 | 
			
		||||
	ADDQ BX,DX
 | 
			
		||||
	MOVQ DX,R10
 | 
			
		||||
	SHRQ $51,DX
 | 
			
		||||
	ANDQ SI,AX
 | 
			
		||||
	IMUL3Q $19,DX,DX
 | 
			
		||||
	ADDQ DX,R8
 | 
			
		||||
	ANDQ SI,R10
 | 
			
		||||
	MOVQ R8,0(DI)
 | 
			
		||||
	MOVQ CX,8(DI)
 | 
			
		||||
	MOVQ R9,16(DI)
 | 
			
		||||
	MOVQ AX,24(DI)
 | 
			
		||||
	MOVQ R10,32(DI)
 | 
			
		||||
	RET
 | 
			
		||||
							
								
								
									
										132
									
								
								vendor/golang.org/x/crypto/curve25519/square_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								vendor/golang.org/x/crypto/curve25519/square_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// This code was translated into a form compatible with 6a from the public
 | 
			
		||||
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
 | 
			
		||||
 | 
			
		||||
// +build amd64,!gccgo,!appengine
 | 
			
		||||
 | 
			
		||||
#include "const_amd64.h"
 | 
			
		||||
 | 
			
		||||
// func square(out, in *[5]uint64)
 | 
			
		||||
TEXT ·square(SB),7,$0-16
 | 
			
		||||
	MOVQ out+0(FP), DI
 | 
			
		||||
	MOVQ in+8(FP), SI
 | 
			
		||||
 | 
			
		||||
	MOVQ 0(SI),AX
 | 
			
		||||
	MULQ 0(SI)
 | 
			
		||||
	MOVQ AX,CX
 | 
			
		||||
	MOVQ DX,R8
 | 
			
		||||
	MOVQ 0(SI),AX
 | 
			
		||||
	SHLQ $1,AX
 | 
			
		||||
	MULQ 8(SI)
 | 
			
		||||
	MOVQ AX,R9
 | 
			
		||||
	MOVQ DX,R10
 | 
			
		||||
	MOVQ 0(SI),AX
 | 
			
		||||
	SHLQ $1,AX
 | 
			
		||||
	MULQ 16(SI)
 | 
			
		||||
	MOVQ AX,R11
 | 
			
		||||
	MOVQ DX,R12
 | 
			
		||||
	MOVQ 0(SI),AX
 | 
			
		||||
	SHLQ $1,AX
 | 
			
		||||
	MULQ 24(SI)
 | 
			
		||||
	MOVQ AX,R13
 | 
			
		||||
	MOVQ DX,R14
 | 
			
		||||
	MOVQ 0(SI),AX
 | 
			
		||||
	SHLQ $1,AX
 | 
			
		||||
	MULQ 32(SI)
 | 
			
		||||
	MOVQ AX,R15
 | 
			
		||||
	MOVQ DX,BX
 | 
			
		||||
	MOVQ 8(SI),AX
 | 
			
		||||
	MULQ 8(SI)
 | 
			
		||||
	ADDQ AX,R11
 | 
			
		||||
	ADCQ DX,R12
 | 
			
		||||
	MOVQ 8(SI),AX
 | 
			
		||||
	SHLQ $1,AX
 | 
			
		||||
	MULQ 16(SI)
 | 
			
		||||
	ADDQ AX,R13
 | 
			
		||||
	ADCQ DX,R14
 | 
			
		||||
	MOVQ 8(SI),AX
 | 
			
		||||
	SHLQ $1,AX
 | 
			
		||||
	MULQ 24(SI)
 | 
			
		||||
	ADDQ AX,R15
 | 
			
		||||
	ADCQ DX,BX
 | 
			
		||||
	MOVQ 8(SI),DX
 | 
			
		||||
	IMUL3Q $38,DX,AX
 | 
			
		||||
	MULQ 32(SI)
 | 
			
		||||
	ADDQ AX,CX
 | 
			
		||||
	ADCQ DX,R8
 | 
			
		||||
	MOVQ 16(SI),AX
 | 
			
		||||
	MULQ 16(SI)
 | 
			
		||||
	ADDQ AX,R15
 | 
			
		||||
	ADCQ DX,BX
 | 
			
		||||
	MOVQ 16(SI),DX
 | 
			
		||||
	IMUL3Q $38,DX,AX
 | 
			
		||||
	MULQ 24(SI)
 | 
			
		||||
	ADDQ AX,CX
 | 
			
		||||
	ADCQ DX,R8
 | 
			
		||||
	MOVQ 16(SI),DX
 | 
			
		||||
	IMUL3Q $38,DX,AX
 | 
			
		||||
	MULQ 32(SI)
 | 
			
		||||
	ADDQ AX,R9
 | 
			
		||||
	ADCQ DX,R10
 | 
			
		||||
	MOVQ 24(SI),DX
 | 
			
		||||
	IMUL3Q $19,DX,AX
 | 
			
		||||
	MULQ 24(SI)
 | 
			
		||||
	ADDQ AX,R9
 | 
			
		||||
	ADCQ DX,R10
 | 
			
		||||
	MOVQ 24(SI),DX
 | 
			
		||||
	IMUL3Q $38,DX,AX
 | 
			
		||||
	MULQ 32(SI)
 | 
			
		||||
	ADDQ AX,R11
 | 
			
		||||
	ADCQ DX,R12
 | 
			
		||||
	MOVQ 32(SI),DX
 | 
			
		||||
	IMUL3Q $19,DX,AX
 | 
			
		||||
	MULQ 32(SI)
 | 
			
		||||
	ADDQ AX,R13
 | 
			
		||||
	ADCQ DX,R14
 | 
			
		||||
	MOVQ $REDMASK51,SI
 | 
			
		||||
	SHLQ $13,R8:CX
 | 
			
		||||
	ANDQ SI,CX
 | 
			
		||||
	SHLQ $13,R10:R9
 | 
			
		||||
	ANDQ SI,R9
 | 
			
		||||
	ADDQ R8,R9
 | 
			
		||||
	SHLQ $13,R12:R11
 | 
			
		||||
	ANDQ SI,R11
 | 
			
		||||
	ADDQ R10,R11
 | 
			
		||||
	SHLQ $13,R14:R13
 | 
			
		||||
	ANDQ SI,R13
 | 
			
		||||
	ADDQ R12,R13
 | 
			
		||||
	SHLQ $13,BX:R15
 | 
			
		||||
	ANDQ SI,R15
 | 
			
		||||
	ADDQ R14,R15
 | 
			
		||||
	IMUL3Q $19,BX,DX
 | 
			
		||||
	ADDQ DX,CX
 | 
			
		||||
	MOVQ CX,DX
 | 
			
		||||
	SHRQ $51,DX
 | 
			
		||||
	ADDQ R9,DX
 | 
			
		||||
	ANDQ SI,CX
 | 
			
		||||
	MOVQ DX,R8
 | 
			
		||||
	SHRQ $51,DX
 | 
			
		||||
	ADDQ R11,DX
 | 
			
		||||
	ANDQ SI,R8
 | 
			
		||||
	MOVQ DX,R9
 | 
			
		||||
	SHRQ $51,DX
 | 
			
		||||
	ADDQ R13,DX
 | 
			
		||||
	ANDQ SI,R9
 | 
			
		||||
	MOVQ DX,AX
 | 
			
		||||
	SHRQ $51,DX
 | 
			
		||||
	ADDQ R15,DX
 | 
			
		||||
	ANDQ SI,AX
 | 
			
		||||
	MOVQ DX,R10
 | 
			
		||||
	SHRQ $51,DX
 | 
			
		||||
	IMUL3Q $19,DX,DX
 | 
			
		||||
	ADDQ DX,CX
 | 
			
		||||
	ANDQ SI,R10
 | 
			
		||||
	MOVQ CX,0(DI)
 | 
			
		||||
	MOVQ R8,8(DI)
 | 
			
		||||
	MOVQ R9,16(DI)
 | 
			
		||||
	MOVQ AX,24(DI)
 | 
			
		||||
	MOVQ R10,32(DI)
 | 
			
		||||
	RET
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/golang.org/x/crypto/ed25519/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/golang.org/x/crypto/ed25519/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
Copyright (c) 2009 The Go Authors. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
met:
 | 
			
		||||
 | 
			
		||||
   * Redistributions of source code must retain the above copyright
 | 
			
		||||
notice, this list of conditions and the following disclaimer.
 | 
			
		||||
   * Redistributions in binary form must reproduce the above
 | 
			
		||||
copyright notice, this list of conditions and the following disclaimer
 | 
			
		||||
in the documentation and/or other materials provided with the
 | 
			
		||||
distribution.
 | 
			
		||||
   * Neither the name of Google Inc. nor the names of its
 | 
			
		||||
contributors may be used to endorse or promote products derived from
 | 
			
		||||
this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
							
								
								
									
										181
									
								
								vendor/golang.org/x/crypto/ed25519/ed25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								vendor/golang.org/x/crypto/ed25519/ed25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,181 @@
 | 
			
		||||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Package ed25519 implements the Ed25519 signature algorithm. See
 | 
			
		||||
// https://ed25519.cr.yp.to/.
 | 
			
		||||
//
 | 
			
		||||
// These functions are also compatible with the “Ed25519” function defined in
 | 
			
		||||
// RFC 8032.
 | 
			
		||||
package ed25519
 | 
			
		||||
 | 
			
		||||
// This code is a port of the public domain, “ref10” implementation of ed25519
 | 
			
		||||
// from SUPERCOP.
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto"
 | 
			
		||||
	cryptorand "crypto/rand"
 | 
			
		||||
	"crypto/sha512"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/ed25519/internal/edwards25519"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// PublicKeySize is the size, in bytes, of public keys as used in this package.
 | 
			
		||||
	PublicKeySize = 32
 | 
			
		||||
	// PrivateKeySize is the size, in bytes, of private keys as used in this package.
 | 
			
		||||
	PrivateKeySize = 64
 | 
			
		||||
	// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
 | 
			
		||||
	SignatureSize = 64
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PublicKey is the type of Ed25519 public keys.
 | 
			
		||||
type PublicKey []byte
 | 
			
		||||
 | 
			
		||||
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
 | 
			
		||||
type PrivateKey []byte
 | 
			
		||||
 | 
			
		||||
// Public returns the PublicKey corresponding to priv.
 | 
			
		||||
func (priv PrivateKey) Public() crypto.PublicKey {
 | 
			
		||||
	publicKey := make([]byte, PublicKeySize)
 | 
			
		||||
	copy(publicKey, priv[32:])
 | 
			
		||||
	return PublicKey(publicKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sign signs the given message with priv.
 | 
			
		||||
// Ed25519 performs two passes over messages to be signed and therefore cannot
 | 
			
		||||
// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
 | 
			
		||||
// indicate the message hasn't been hashed. This can be achieved by passing
 | 
			
		||||
// crypto.Hash(0) as the value for opts.
 | 
			
		||||
func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
 | 
			
		||||
	if opts.HashFunc() != crypto.Hash(0) {
 | 
			
		||||
		return nil, errors.New("ed25519: cannot sign hashed message")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Sign(priv, message), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenerateKey generates a public/private key pair using entropy from rand.
 | 
			
		||||
// If rand is nil, crypto/rand.Reader will be used.
 | 
			
		||||
func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) {
 | 
			
		||||
	if rand == nil {
 | 
			
		||||
		rand = cryptorand.Reader
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	privateKey = make([]byte, PrivateKeySize)
 | 
			
		||||
	publicKey = make([]byte, PublicKeySize)
 | 
			
		||||
	_, err = io.ReadFull(rand, privateKey[:32])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	digest := sha512.Sum512(privateKey[:32])
 | 
			
		||||
	digest[0] &= 248
 | 
			
		||||
	digest[31] &= 127
 | 
			
		||||
	digest[31] |= 64
 | 
			
		||||
 | 
			
		||||
	var A edwards25519.ExtendedGroupElement
 | 
			
		||||
	var hBytes [32]byte
 | 
			
		||||
	copy(hBytes[:], digest[:])
 | 
			
		||||
	edwards25519.GeScalarMultBase(&A, &hBytes)
 | 
			
		||||
	var publicKeyBytes [32]byte
 | 
			
		||||
	A.ToBytes(&publicKeyBytes)
 | 
			
		||||
 | 
			
		||||
	copy(privateKey[32:], publicKeyBytes[:])
 | 
			
		||||
	copy(publicKey, publicKeyBytes[:])
 | 
			
		||||
 | 
			
		||||
	return publicKey, privateKey, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sign signs the message with privateKey and returns a signature. It will
 | 
			
		||||
// panic if len(privateKey) is not PrivateKeySize.
 | 
			
		||||
func Sign(privateKey PrivateKey, message []byte) []byte {
 | 
			
		||||
	if l := len(privateKey); l != PrivateKeySize {
 | 
			
		||||
		panic("ed25519: bad private key length: " + strconv.Itoa(l))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	h := sha512.New()
 | 
			
		||||
	h.Write(privateKey[:32])
 | 
			
		||||
 | 
			
		||||
	var digest1, messageDigest, hramDigest [64]byte
 | 
			
		||||
	var expandedSecretKey [32]byte
 | 
			
		||||
	h.Sum(digest1[:0])
 | 
			
		||||
	copy(expandedSecretKey[:], digest1[:])
 | 
			
		||||
	expandedSecretKey[0] &= 248
 | 
			
		||||
	expandedSecretKey[31] &= 63
 | 
			
		||||
	expandedSecretKey[31] |= 64
 | 
			
		||||
 | 
			
		||||
	h.Reset()
 | 
			
		||||
	h.Write(digest1[32:])
 | 
			
		||||
	h.Write(message)
 | 
			
		||||
	h.Sum(messageDigest[:0])
 | 
			
		||||
 | 
			
		||||
	var messageDigestReduced [32]byte
 | 
			
		||||
	edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
 | 
			
		||||
	var R edwards25519.ExtendedGroupElement
 | 
			
		||||
	edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
 | 
			
		||||
 | 
			
		||||
	var encodedR [32]byte
 | 
			
		||||
	R.ToBytes(&encodedR)
 | 
			
		||||
 | 
			
		||||
	h.Reset()
 | 
			
		||||
	h.Write(encodedR[:])
 | 
			
		||||
	h.Write(privateKey[32:])
 | 
			
		||||
	h.Write(message)
 | 
			
		||||
	h.Sum(hramDigest[:0])
 | 
			
		||||
	var hramDigestReduced [32]byte
 | 
			
		||||
	edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
 | 
			
		||||
 | 
			
		||||
	var s [32]byte
 | 
			
		||||
	edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
 | 
			
		||||
 | 
			
		||||
	signature := make([]byte, SignatureSize)
 | 
			
		||||
	copy(signature[:], encodedR[:])
 | 
			
		||||
	copy(signature[32:], s[:])
 | 
			
		||||
 | 
			
		||||
	return signature
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Verify reports whether sig is a valid signature of message by publicKey. It
 | 
			
		||||
// will panic if len(publicKey) is not PublicKeySize.
 | 
			
		||||
func Verify(publicKey PublicKey, message, sig []byte) bool {
 | 
			
		||||
	if l := len(publicKey); l != PublicKeySize {
 | 
			
		||||
		panic("ed25519: bad public key length: " + strconv.Itoa(l))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(sig) != SignatureSize || sig[63]&224 != 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var A edwards25519.ExtendedGroupElement
 | 
			
		||||
	var publicKeyBytes [32]byte
 | 
			
		||||
	copy(publicKeyBytes[:], publicKey)
 | 
			
		||||
	if !A.FromBytes(&publicKeyBytes) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	edwards25519.FeNeg(&A.X, &A.X)
 | 
			
		||||
	edwards25519.FeNeg(&A.T, &A.T)
 | 
			
		||||
 | 
			
		||||
	h := sha512.New()
 | 
			
		||||
	h.Write(sig[:32])
 | 
			
		||||
	h.Write(publicKey[:])
 | 
			
		||||
	h.Write(message)
 | 
			
		||||
	var digest [64]byte
 | 
			
		||||
	h.Sum(digest[:0])
 | 
			
		||||
 | 
			
		||||
	var hReduced [32]byte
 | 
			
		||||
	edwards25519.ScReduce(&hReduced, &digest)
 | 
			
		||||
 | 
			
		||||
	var R edwards25519.ProjectiveGroupElement
 | 
			
		||||
	var b [32]byte
 | 
			
		||||
	copy(b[:], sig[32:])
 | 
			
		||||
	edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
 | 
			
		||||
 | 
			
		||||
	var checkR [32]byte
 | 
			
		||||
	R.ToBytes(&checkR)
 | 
			
		||||
	return bytes.Equal(sig[:32], checkR[:])
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1422
									
								
								vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1422
									
								
								vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1771
									
								
								vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1771
									
								
								vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										27
									
								
								vendor/golang.org/x/crypto/ssh/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/golang.org/x/crypto/ssh/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
Copyright (c) 2009 The Go Authors. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
met:
 | 
			
		||||
 | 
			
		||||
   * Redistributions of source code must retain the above copyright
 | 
			
		||||
notice, this list of conditions and the following disclaimer.
 | 
			
		||||
   * Redistributions in binary form must reproduce the above
 | 
			
		||||
copyright notice, this list of conditions and the following disclaimer
 | 
			
		||||
in the documentation and/or other materials provided with the
 | 
			
		||||
distribution.
 | 
			
		||||
   * Neither the name of Google Inc. nor the names of its
 | 
			
		||||
contributors may be used to endorse or promote products derived from
 | 
			
		||||
this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
							
								
								
									
										683
									
								
								vendor/golang.org/x/crypto/ssh/agent/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										683
									
								
								vendor/golang.org/x/crypto/ssh/agent/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,683 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Package agent implements the ssh-agent protocol, and provides both
 | 
			
		||||
// a client and a server. The client can talk to a standard ssh-agent
 | 
			
		||||
// that uses UNIX sockets, and one could implement an alternative
 | 
			
		||||
// ssh-agent process using the sample server.
 | 
			
		||||
//
 | 
			
		||||
// References:
 | 
			
		||||
//  [PROTOCOL.agent]:    http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD
 | 
			
		||||
package agent // import "golang.org/x/crypto/ssh/agent"
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/dsa"
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/ed25519"
 | 
			
		||||
	"golang.org/x/crypto/ssh"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Agent represents the capabilities of an ssh-agent.
 | 
			
		||||
type Agent interface {
 | 
			
		||||
	// List returns the identities known to the agent.
 | 
			
		||||
	List() ([]*Key, error)
 | 
			
		||||
 | 
			
		||||
	// Sign has the agent sign the data using a protocol 2 key as defined
 | 
			
		||||
	// in [PROTOCOL.agent] section 2.6.2.
 | 
			
		||||
	Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error)
 | 
			
		||||
 | 
			
		||||
	// Add adds a private key to the agent.
 | 
			
		||||
	Add(key AddedKey) error
 | 
			
		||||
 | 
			
		||||
	// Remove removes all identities with the given public key.
 | 
			
		||||
	Remove(key ssh.PublicKey) error
 | 
			
		||||
 | 
			
		||||
	// RemoveAll removes all identities.
 | 
			
		||||
	RemoveAll() error
 | 
			
		||||
 | 
			
		||||
	// Lock locks the agent. Sign and Remove will fail, and List will empty an empty list.
 | 
			
		||||
	Lock(passphrase []byte) error
 | 
			
		||||
 | 
			
		||||
	// Unlock undoes the effect of Lock
 | 
			
		||||
	Unlock(passphrase []byte) error
 | 
			
		||||
 | 
			
		||||
	// Signers returns signers for all the known keys.
 | 
			
		||||
	Signers() ([]ssh.Signer, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConstraintExtension describes an optional constraint defined by users.
 | 
			
		||||
type ConstraintExtension struct {
 | 
			
		||||
	// ExtensionName consist of a UTF-8 string suffixed by the
 | 
			
		||||
	// implementation domain following the naming scheme defined
 | 
			
		||||
	// in Section 4.2 of [RFC4251], e.g.  "foo@example.com".
 | 
			
		||||
	ExtensionName string
 | 
			
		||||
	// ExtensionDetails contains the actual content of the extended
 | 
			
		||||
	// constraint.
 | 
			
		||||
	ExtensionDetails []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddedKey describes an SSH key to be added to an Agent.
 | 
			
		||||
type AddedKey struct {
 | 
			
		||||
	// PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or
 | 
			
		||||
	// *ecdsa.PrivateKey, which will be inserted into the agent.
 | 
			
		||||
	PrivateKey interface{}
 | 
			
		||||
	// Certificate, if not nil, is communicated to the agent and will be
 | 
			
		||||
	// stored with the key.
 | 
			
		||||
	Certificate *ssh.Certificate
 | 
			
		||||
	// Comment is an optional, free-form string.
 | 
			
		||||
	Comment string
 | 
			
		||||
	// LifetimeSecs, if not zero, is the number of seconds that the
 | 
			
		||||
	// agent will store the key for.
 | 
			
		||||
	LifetimeSecs uint32
 | 
			
		||||
	// ConfirmBeforeUse, if true, requests that the agent confirm with the
 | 
			
		||||
	// user before each use of this key.
 | 
			
		||||
	ConfirmBeforeUse bool
 | 
			
		||||
	// ConstraintExtensions are the experimental or private-use constraints
 | 
			
		||||
	// defined by users.
 | 
			
		||||
	ConstraintExtensions []ConstraintExtension
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See [PROTOCOL.agent], section 3.
 | 
			
		||||
const (
 | 
			
		||||
	agentRequestV1Identities   = 1
 | 
			
		||||
	agentRemoveAllV1Identities = 9
 | 
			
		||||
 | 
			
		||||
	// 3.2 Requests from client to agent for protocol 2 key operations
 | 
			
		||||
	agentAddIdentity         = 17
 | 
			
		||||
	agentRemoveIdentity      = 18
 | 
			
		||||
	agentRemoveAllIdentities = 19
 | 
			
		||||
	agentAddIDConstrained    = 25
 | 
			
		||||
 | 
			
		||||
	// 3.3 Key-type independent requests from client to agent
 | 
			
		||||
	agentAddSmartcardKey            = 20
 | 
			
		||||
	agentRemoveSmartcardKey         = 21
 | 
			
		||||
	agentLock                       = 22
 | 
			
		||||
	agentUnlock                     = 23
 | 
			
		||||
	agentAddSmartcardKeyConstrained = 26
 | 
			
		||||
 | 
			
		||||
	// 3.7 Key constraint identifiers
 | 
			
		||||
	agentConstrainLifetime  = 1
 | 
			
		||||
	agentConstrainConfirm   = 2
 | 
			
		||||
	agentConstrainExtension = 3
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// maxAgentResponseBytes is the maximum agent reply size that is accepted. This
 | 
			
		||||
// is a sanity check, not a limit in the spec.
 | 
			
		||||
const maxAgentResponseBytes = 16 << 20
 | 
			
		||||
 | 
			
		||||
// Agent messages:
 | 
			
		||||
// These structures mirror the wire format of the corresponding ssh agent
 | 
			
		||||
// messages found in [PROTOCOL.agent].
 | 
			
		||||
 | 
			
		||||
// 3.4 Generic replies from agent to client
 | 
			
		||||
const agentFailure = 5
 | 
			
		||||
 | 
			
		||||
type failureAgentMsg struct{}
 | 
			
		||||
 | 
			
		||||
const agentSuccess = 6
 | 
			
		||||
 | 
			
		||||
type successAgentMsg struct{}
 | 
			
		||||
 | 
			
		||||
// See [PROTOCOL.agent], section 2.5.2.
 | 
			
		||||
const agentRequestIdentities = 11
 | 
			
		||||
 | 
			
		||||
type requestIdentitiesAgentMsg struct{}
 | 
			
		||||
 | 
			
		||||
// See [PROTOCOL.agent], section 2.5.2.
 | 
			
		||||
const agentIdentitiesAnswer = 12
 | 
			
		||||
 | 
			
		||||
type identitiesAnswerAgentMsg struct {
 | 
			
		||||
	NumKeys uint32 `sshtype:"12"`
 | 
			
		||||
	Keys    []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See [PROTOCOL.agent], section 2.6.2.
 | 
			
		||||
const agentSignRequest = 13
 | 
			
		||||
 | 
			
		||||
type signRequestAgentMsg struct {
 | 
			
		||||
	KeyBlob []byte `sshtype:"13"`
 | 
			
		||||
	Data    []byte
 | 
			
		||||
	Flags   uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See [PROTOCOL.agent], section 2.6.2.
 | 
			
		||||
 | 
			
		||||
// 3.6 Replies from agent to client for protocol 2 key operations
 | 
			
		||||
const agentSignResponse = 14
 | 
			
		||||
 | 
			
		||||
type signResponseAgentMsg struct {
 | 
			
		||||
	SigBlob []byte `sshtype:"14"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type publicKey struct {
 | 
			
		||||
	Format string
 | 
			
		||||
	Rest   []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 3.7 Key constraint identifiers
 | 
			
		||||
type constrainLifetimeAgentMsg struct {
 | 
			
		||||
	LifetimeSecs uint32 `sshtype:"1"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type constrainExtensionAgentMsg struct {
 | 
			
		||||
	ExtensionName    string `sshtype:"3"`
 | 
			
		||||
	ExtensionDetails []byte
 | 
			
		||||
 | 
			
		||||
	// Rest is a field used for parsing, not part of message
 | 
			
		||||
	Rest []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Key represents a protocol 2 public key as defined in
 | 
			
		||||
// [PROTOCOL.agent], section 2.5.2.
 | 
			
		||||
type Key struct {
 | 
			
		||||
	Format  string
 | 
			
		||||
	Blob    []byte
 | 
			
		||||
	Comment string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func clientErr(err error) error {
 | 
			
		||||
	return fmt.Errorf("agent: client error: %v", err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String returns the storage form of an agent key with the format, base64
 | 
			
		||||
// encoded serialized key, and the comment if it is not empty.
 | 
			
		||||
func (k *Key) String() string {
 | 
			
		||||
	s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob)
 | 
			
		||||
 | 
			
		||||
	if k.Comment != "" {
 | 
			
		||||
		s += " " + k.Comment
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Type returns the public key type.
 | 
			
		||||
func (k *Key) Type() string {
 | 
			
		||||
	return k.Format
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Marshal returns key blob to satisfy the ssh.PublicKey interface.
 | 
			
		||||
func (k *Key) Marshal() []byte {
 | 
			
		||||
	return k.Blob
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Verify satisfies the ssh.PublicKey interface.
 | 
			
		||||
func (k *Key) Verify(data []byte, sig *ssh.Signature) error {
 | 
			
		||||
	pubKey, err := ssh.ParsePublicKey(k.Blob)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("agent: bad public key: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return pubKey.Verify(data, sig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type wireKey struct {
 | 
			
		||||
	Format string
 | 
			
		||||
	Rest   []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseKey(in []byte) (out *Key, rest []byte, err error) {
 | 
			
		||||
	var record struct {
 | 
			
		||||
		Blob    []byte
 | 
			
		||||
		Comment string
 | 
			
		||||
		Rest    []byte `ssh:"rest"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := ssh.Unmarshal(in, &record); err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var wk wireKey
 | 
			
		||||
	if err := ssh.Unmarshal(record.Blob, &wk); err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &Key{
 | 
			
		||||
		Format:  wk.Format,
 | 
			
		||||
		Blob:    record.Blob,
 | 
			
		||||
		Comment: record.Comment,
 | 
			
		||||
	}, record.Rest, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// client is a client for an ssh-agent process.
 | 
			
		||||
type client struct {
 | 
			
		||||
	// conn is typically a *net.UnixConn
 | 
			
		||||
	conn io.ReadWriter
 | 
			
		||||
	// mu is used to prevent concurrent access to the agent
 | 
			
		||||
	mu sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClient returns an Agent that talks to an ssh-agent process over
 | 
			
		||||
// the given connection.
 | 
			
		||||
func NewClient(rw io.ReadWriter) Agent {
 | 
			
		||||
	return &client{conn: rw}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// call sends an RPC to the agent. On success, the reply is
 | 
			
		||||
// unmarshaled into reply and replyType is set to the first byte of
 | 
			
		||||
// the reply, which contains the type of the message.
 | 
			
		||||
func (c *client) call(req []byte) (reply interface{}, err error) {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	msg := make([]byte, 4+len(req))
 | 
			
		||||
	binary.BigEndian.PutUint32(msg, uint32(len(req)))
 | 
			
		||||
	copy(msg[4:], req)
 | 
			
		||||
	if _, err = c.conn.Write(msg); err != nil {
 | 
			
		||||
		return nil, clientErr(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var respSizeBuf [4]byte
 | 
			
		||||
	if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil {
 | 
			
		||||
		return nil, clientErr(err)
 | 
			
		||||
	}
 | 
			
		||||
	respSize := binary.BigEndian.Uint32(respSizeBuf[:])
 | 
			
		||||
	if respSize > maxAgentResponseBytes {
 | 
			
		||||
		return nil, clientErr(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf := make([]byte, respSize)
 | 
			
		||||
	if _, err = io.ReadFull(c.conn, buf); err != nil {
 | 
			
		||||
		return nil, clientErr(err)
 | 
			
		||||
	}
 | 
			
		||||
	reply, err = unmarshal(buf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, clientErr(err)
 | 
			
		||||
	}
 | 
			
		||||
	return reply, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) simpleCall(req []byte) error {
 | 
			
		||||
	resp, err := c.call(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := resp.(*successAgentMsg); ok {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return errors.New("agent: failure")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) RemoveAll() error {
 | 
			
		||||
	return c.simpleCall([]byte{agentRemoveAllIdentities})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) Remove(key ssh.PublicKey) error {
 | 
			
		||||
	req := ssh.Marshal(&agentRemoveIdentityMsg{
 | 
			
		||||
		KeyBlob: key.Marshal(),
 | 
			
		||||
	})
 | 
			
		||||
	return c.simpleCall(req)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) Lock(passphrase []byte) error {
 | 
			
		||||
	req := ssh.Marshal(&agentLockMsg{
 | 
			
		||||
		Passphrase: passphrase,
 | 
			
		||||
	})
 | 
			
		||||
	return c.simpleCall(req)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) Unlock(passphrase []byte) error {
 | 
			
		||||
	req := ssh.Marshal(&agentUnlockMsg{
 | 
			
		||||
		Passphrase: passphrase,
 | 
			
		||||
	})
 | 
			
		||||
	return c.simpleCall(req)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// List returns the identities known to the agent.
 | 
			
		||||
func (c *client) List() ([]*Key, error) {
 | 
			
		||||
	// see [PROTOCOL.agent] section 2.5.2.
 | 
			
		||||
	req := []byte{agentRequestIdentities}
 | 
			
		||||
 | 
			
		||||
	msg, err := c.call(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch msg := msg.(type) {
 | 
			
		||||
	case *identitiesAnswerAgentMsg:
 | 
			
		||||
		if msg.NumKeys > maxAgentResponseBytes/8 {
 | 
			
		||||
			return nil, errors.New("agent: too many keys in agent reply")
 | 
			
		||||
		}
 | 
			
		||||
		keys := make([]*Key, msg.NumKeys)
 | 
			
		||||
		data := msg.Keys
 | 
			
		||||
		for i := uint32(0); i < msg.NumKeys; i++ {
 | 
			
		||||
			var key *Key
 | 
			
		||||
			var err error
 | 
			
		||||
			if key, data, err = parseKey(data); err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			keys[i] = key
 | 
			
		||||
		}
 | 
			
		||||
		return keys, nil
 | 
			
		||||
	case *failureAgentMsg:
 | 
			
		||||
		return nil, errors.New("agent: failed to list keys")
 | 
			
		||||
	}
 | 
			
		||||
	panic("unreachable")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sign has the agent sign the data using a protocol 2 key as defined
 | 
			
		||||
// in [PROTOCOL.agent] section 2.6.2.
 | 
			
		||||
func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
 | 
			
		||||
	req := ssh.Marshal(signRequestAgentMsg{
 | 
			
		||||
		KeyBlob: key.Marshal(),
 | 
			
		||||
		Data:    data,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	msg, err := c.call(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch msg := msg.(type) {
 | 
			
		||||
	case *signResponseAgentMsg:
 | 
			
		||||
		var sig ssh.Signature
 | 
			
		||||
		if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return &sig, nil
 | 
			
		||||
	case *failureAgentMsg:
 | 
			
		||||
		return nil, errors.New("agent: failed to sign challenge")
 | 
			
		||||
	}
 | 
			
		||||
	panic("unreachable")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unmarshal parses an agent message in packet, returning the parsed
 | 
			
		||||
// form and the message type of packet.
 | 
			
		||||
func unmarshal(packet []byte) (interface{}, error) {
 | 
			
		||||
	if len(packet) < 1 {
 | 
			
		||||
		return nil, errors.New("agent: empty packet")
 | 
			
		||||
	}
 | 
			
		||||
	var msg interface{}
 | 
			
		||||
	switch packet[0] {
 | 
			
		||||
	case agentFailure:
 | 
			
		||||
		return new(failureAgentMsg), nil
 | 
			
		||||
	case agentSuccess:
 | 
			
		||||
		return new(successAgentMsg), nil
 | 
			
		||||
	case agentIdentitiesAnswer:
 | 
			
		||||
		msg = new(identitiesAnswerAgentMsg)
 | 
			
		||||
	case agentSignResponse:
 | 
			
		||||
		msg = new(signResponseAgentMsg)
 | 
			
		||||
	case agentV1IdentitiesAnswer:
 | 
			
		||||
		msg = new(agentV1IdentityMsg)
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("agent: unknown type tag %d", packet[0])
 | 
			
		||||
	}
 | 
			
		||||
	if err := ssh.Unmarshal(packet, msg); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return msg, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rsaKeyMsg struct {
 | 
			
		||||
	Type        string `sshtype:"17|25"`
 | 
			
		||||
	N           *big.Int
 | 
			
		||||
	E           *big.Int
 | 
			
		||||
	D           *big.Int
 | 
			
		||||
	Iqmp        *big.Int // IQMP = Inverse Q Mod P
 | 
			
		||||
	P           *big.Int
 | 
			
		||||
	Q           *big.Int
 | 
			
		||||
	Comments    string
 | 
			
		||||
	Constraints []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type dsaKeyMsg struct {
 | 
			
		||||
	Type        string `sshtype:"17|25"`
 | 
			
		||||
	P           *big.Int
 | 
			
		||||
	Q           *big.Int
 | 
			
		||||
	G           *big.Int
 | 
			
		||||
	Y           *big.Int
 | 
			
		||||
	X           *big.Int
 | 
			
		||||
	Comments    string
 | 
			
		||||
	Constraints []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ecdsaKeyMsg struct {
 | 
			
		||||
	Type        string `sshtype:"17|25"`
 | 
			
		||||
	Curve       string
 | 
			
		||||
	KeyBytes    []byte
 | 
			
		||||
	D           *big.Int
 | 
			
		||||
	Comments    string
 | 
			
		||||
	Constraints []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ed25519KeyMsg struct {
 | 
			
		||||
	Type        string `sshtype:"17|25"`
 | 
			
		||||
	Pub         []byte
 | 
			
		||||
	Priv        []byte
 | 
			
		||||
	Comments    string
 | 
			
		||||
	Constraints []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Insert adds a private key to the agent.
 | 
			
		||||
func (c *client) insertKey(s interface{}, comment string, constraints []byte) error {
 | 
			
		||||
	var req []byte
 | 
			
		||||
	switch k := s.(type) {
 | 
			
		||||
	case *rsa.PrivateKey:
 | 
			
		||||
		if len(k.Primes) != 2 {
 | 
			
		||||
			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
 | 
			
		||||
		}
 | 
			
		||||
		k.Precompute()
 | 
			
		||||
		req = ssh.Marshal(rsaKeyMsg{
 | 
			
		||||
			Type:        ssh.KeyAlgoRSA,
 | 
			
		||||
			N:           k.N,
 | 
			
		||||
			E:           big.NewInt(int64(k.E)),
 | 
			
		||||
			D:           k.D,
 | 
			
		||||
			Iqmp:        k.Precomputed.Qinv,
 | 
			
		||||
			P:           k.Primes[0],
 | 
			
		||||
			Q:           k.Primes[1],
 | 
			
		||||
			Comments:    comment,
 | 
			
		||||
			Constraints: constraints,
 | 
			
		||||
		})
 | 
			
		||||
	case *dsa.PrivateKey:
 | 
			
		||||
		req = ssh.Marshal(dsaKeyMsg{
 | 
			
		||||
			Type:        ssh.KeyAlgoDSA,
 | 
			
		||||
			P:           k.P,
 | 
			
		||||
			Q:           k.Q,
 | 
			
		||||
			G:           k.G,
 | 
			
		||||
			Y:           k.Y,
 | 
			
		||||
			X:           k.X,
 | 
			
		||||
			Comments:    comment,
 | 
			
		||||
			Constraints: constraints,
 | 
			
		||||
		})
 | 
			
		||||
	case *ecdsa.PrivateKey:
 | 
			
		||||
		nistID := fmt.Sprintf("nistp%d", k.Params().BitSize)
 | 
			
		||||
		req = ssh.Marshal(ecdsaKeyMsg{
 | 
			
		||||
			Type:        "ecdsa-sha2-" + nistID,
 | 
			
		||||
			Curve:       nistID,
 | 
			
		||||
			KeyBytes:    elliptic.Marshal(k.Curve, k.X, k.Y),
 | 
			
		||||
			D:           k.D,
 | 
			
		||||
			Comments:    comment,
 | 
			
		||||
			Constraints: constraints,
 | 
			
		||||
		})
 | 
			
		||||
	case *ed25519.PrivateKey:
 | 
			
		||||
		req = ssh.Marshal(ed25519KeyMsg{
 | 
			
		||||
			Type:        ssh.KeyAlgoED25519,
 | 
			
		||||
			Pub:         []byte(*k)[32:],
 | 
			
		||||
			Priv:        []byte(*k),
 | 
			
		||||
			Comments:    comment,
 | 
			
		||||
			Constraints: constraints,
 | 
			
		||||
		})
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("agent: unsupported key type %T", s)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if constraints are present then the message type needs to be changed.
 | 
			
		||||
	if len(constraints) != 0 {
 | 
			
		||||
		req[0] = agentAddIDConstrained
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp, err := c.call(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := resp.(*successAgentMsg); ok {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return errors.New("agent: failure")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rsaCertMsg struct {
 | 
			
		||||
	Type        string `sshtype:"17|25"`
 | 
			
		||||
	CertBytes   []byte
 | 
			
		||||
	D           *big.Int
 | 
			
		||||
	Iqmp        *big.Int // IQMP = Inverse Q Mod P
 | 
			
		||||
	P           *big.Int
 | 
			
		||||
	Q           *big.Int
 | 
			
		||||
	Comments    string
 | 
			
		||||
	Constraints []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type dsaCertMsg struct {
 | 
			
		||||
	Type        string `sshtype:"17|25"`
 | 
			
		||||
	CertBytes   []byte
 | 
			
		||||
	X           *big.Int
 | 
			
		||||
	Comments    string
 | 
			
		||||
	Constraints []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ecdsaCertMsg struct {
 | 
			
		||||
	Type        string `sshtype:"17|25"`
 | 
			
		||||
	CertBytes   []byte
 | 
			
		||||
	D           *big.Int
 | 
			
		||||
	Comments    string
 | 
			
		||||
	Constraints []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ed25519CertMsg struct {
 | 
			
		||||
	Type        string `sshtype:"17|25"`
 | 
			
		||||
	CertBytes   []byte
 | 
			
		||||
	Pub         []byte
 | 
			
		||||
	Priv        []byte
 | 
			
		||||
	Comments    string
 | 
			
		||||
	Constraints []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add adds a private key to the agent. If a certificate is given,
 | 
			
		||||
// that certificate is added instead as public key.
 | 
			
		||||
func (c *client) Add(key AddedKey) error {
 | 
			
		||||
	var constraints []byte
 | 
			
		||||
 | 
			
		||||
	if secs := key.LifetimeSecs; secs != 0 {
 | 
			
		||||
		constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if key.ConfirmBeforeUse {
 | 
			
		||||
		constraints = append(constraints, agentConstrainConfirm)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cert := key.Certificate
 | 
			
		||||
	if cert == nil {
 | 
			
		||||
		return c.insertKey(key.PrivateKey, key.Comment, constraints)
 | 
			
		||||
	}
 | 
			
		||||
	return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error {
 | 
			
		||||
	var req []byte
 | 
			
		||||
	switch k := s.(type) {
 | 
			
		||||
	case *rsa.PrivateKey:
 | 
			
		||||
		if len(k.Primes) != 2 {
 | 
			
		||||
			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
 | 
			
		||||
		}
 | 
			
		||||
		k.Precompute()
 | 
			
		||||
		req = ssh.Marshal(rsaCertMsg{
 | 
			
		||||
			Type:        cert.Type(),
 | 
			
		||||
			CertBytes:   cert.Marshal(),
 | 
			
		||||
			D:           k.D,
 | 
			
		||||
			Iqmp:        k.Precomputed.Qinv,
 | 
			
		||||
			P:           k.Primes[0],
 | 
			
		||||
			Q:           k.Primes[1],
 | 
			
		||||
			Comments:    comment,
 | 
			
		||||
			Constraints: constraints,
 | 
			
		||||
		})
 | 
			
		||||
	case *dsa.PrivateKey:
 | 
			
		||||
		req = ssh.Marshal(dsaCertMsg{
 | 
			
		||||
			Type:        cert.Type(),
 | 
			
		||||
			CertBytes:   cert.Marshal(),
 | 
			
		||||
			X:           k.X,
 | 
			
		||||
			Comments:    comment,
 | 
			
		||||
			Constraints: constraints,
 | 
			
		||||
		})
 | 
			
		||||
	case *ecdsa.PrivateKey:
 | 
			
		||||
		req = ssh.Marshal(ecdsaCertMsg{
 | 
			
		||||
			Type:        cert.Type(),
 | 
			
		||||
			CertBytes:   cert.Marshal(),
 | 
			
		||||
			D:           k.D,
 | 
			
		||||
			Comments:    comment,
 | 
			
		||||
			Constraints: constraints,
 | 
			
		||||
		})
 | 
			
		||||
	case *ed25519.PrivateKey:
 | 
			
		||||
		req = ssh.Marshal(ed25519CertMsg{
 | 
			
		||||
			Type:        cert.Type(),
 | 
			
		||||
			CertBytes:   cert.Marshal(),
 | 
			
		||||
			Pub:         []byte(*k)[32:],
 | 
			
		||||
			Priv:        []byte(*k),
 | 
			
		||||
			Comments:    comment,
 | 
			
		||||
			Constraints: constraints,
 | 
			
		||||
		})
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("agent: unsupported key type %T", s)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if constraints are present then the message type needs to be changed.
 | 
			
		||||
	if len(constraints) != 0 {
 | 
			
		||||
		req[0] = agentAddIDConstrained
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	signer, err := ssh.NewSignerFromKey(s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 {
 | 
			
		||||
		return errors.New("agent: signer and cert have different public key")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp, err := c.call(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := resp.(*successAgentMsg); ok {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return errors.New("agent: failure")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Signers provides a callback for client authentication.
 | 
			
		||||
func (c *client) Signers() ([]ssh.Signer, error) {
 | 
			
		||||
	keys, err := c.List()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var result []ssh.Signer
 | 
			
		||||
	for _, k := range keys {
 | 
			
		||||
		result = append(result, &agentKeyringSigner{c, k})
 | 
			
		||||
	}
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type agentKeyringSigner struct {
 | 
			
		||||
	agent *client
 | 
			
		||||
	pub   ssh.PublicKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *agentKeyringSigner) PublicKey() ssh.PublicKey {
 | 
			
		||||
	return s.pub
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
 | 
			
		||||
	// The agent has its own entropy source, so the rand argument is ignored.
 | 
			
		||||
	return s.agent.Sign(s.pub, data)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										103
									
								
								vendor/golang.org/x/crypto/ssh/agent/forward.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								vendor/golang.org/x/crypto/ssh/agent/forward.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package agent
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/ssh"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RequestAgentForwarding sets up agent forwarding for the session.
 | 
			
		||||
// ForwardToAgent or ForwardToRemote should be called to route
 | 
			
		||||
// the authentication requests.
 | 
			
		||||
func RequestAgentForwarding(session *ssh.Session) error {
 | 
			
		||||
	ok, err := session.SendRequest("auth-agent-req@openssh.com", true, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return errors.New("forwarding request denied")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ForwardToAgent routes authentication requests to the given keyring.
 | 
			
		||||
func ForwardToAgent(client *ssh.Client, keyring Agent) error {
 | 
			
		||||
	channels := client.HandleChannelOpen(channelType)
 | 
			
		||||
	if channels == nil {
 | 
			
		||||
		return errors.New("agent: already have handler for " + channelType)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for ch := range channels {
 | 
			
		||||
			channel, reqs, err := ch.Accept()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			go ssh.DiscardRequests(reqs)
 | 
			
		||||
			go func() {
 | 
			
		||||
				ServeAgent(keyring, channel)
 | 
			
		||||
				channel.Close()
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const channelType = "auth-agent@openssh.com"
 | 
			
		||||
 | 
			
		||||
// ForwardToRemote routes authentication requests to the ssh-agent
 | 
			
		||||
// process serving on the given unix socket.
 | 
			
		||||
func ForwardToRemote(client *ssh.Client, addr string) error {
 | 
			
		||||
	channels := client.HandleChannelOpen(channelType)
 | 
			
		||||
	if channels == nil {
 | 
			
		||||
		return errors.New("agent: already have handler for " + channelType)
 | 
			
		||||
	}
 | 
			
		||||
	conn, err := net.Dial("unix", addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	conn.Close()
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for ch := range channels {
 | 
			
		||||
			channel, reqs, err := ch.Accept()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			go ssh.DiscardRequests(reqs)
 | 
			
		||||
			go forwardUnixSocket(channel, addr)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func forwardUnixSocket(channel ssh.Channel, addr string) {
 | 
			
		||||
	conn, err := net.Dial("unix", addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	wg.Add(2)
 | 
			
		||||
	go func() {
 | 
			
		||||
		io.Copy(conn, channel)
 | 
			
		||||
		conn.(*net.UnixConn).CloseWrite()
 | 
			
		||||
		wg.Done()
 | 
			
		||||
	}()
 | 
			
		||||
	go func() {
 | 
			
		||||
		io.Copy(channel, conn)
 | 
			
		||||
		channel.CloseWrite()
 | 
			
		||||
		wg.Done()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
	conn.Close()
 | 
			
		||||
	channel.Close()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										215
									
								
								vendor/golang.org/x/crypto/ssh/agent/keyring.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								vendor/golang.org/x/crypto/ssh/agent/keyring.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,215 @@
 | 
			
		||||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package agent
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/subtle"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/ssh"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type privKey struct {
 | 
			
		||||
	signer  ssh.Signer
 | 
			
		||||
	comment string
 | 
			
		||||
	expire  *time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type keyring struct {
 | 
			
		||||
	mu   sync.Mutex
 | 
			
		||||
	keys []privKey
 | 
			
		||||
 | 
			
		||||
	locked     bool
 | 
			
		||||
	passphrase []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errLocked = errors.New("agent: locked")
 | 
			
		||||
 | 
			
		||||
// NewKeyring returns an Agent that holds keys in memory.  It is safe
 | 
			
		||||
// for concurrent use by multiple goroutines.
 | 
			
		||||
func NewKeyring() Agent {
 | 
			
		||||
	return &keyring{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveAll removes all identities.
 | 
			
		||||
func (r *keyring) RemoveAll() error {
 | 
			
		||||
	r.mu.Lock()
 | 
			
		||||
	defer r.mu.Unlock()
 | 
			
		||||
	if r.locked {
 | 
			
		||||
		return errLocked
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.keys = nil
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// removeLocked does the actual key removal. The caller must already be holding the
 | 
			
		||||
// keyring mutex.
 | 
			
		||||
func (r *keyring) removeLocked(want []byte) error {
 | 
			
		||||
	found := false
 | 
			
		||||
	for i := 0; i < len(r.keys); {
 | 
			
		||||
		if bytes.Equal(r.keys[i].signer.PublicKey().Marshal(), want) {
 | 
			
		||||
			found = true
 | 
			
		||||
			r.keys[i] = r.keys[len(r.keys)-1]
 | 
			
		||||
			r.keys = r.keys[:len(r.keys)-1]
 | 
			
		||||
			continue
 | 
			
		||||
		} else {
 | 
			
		||||
			i++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !found {
 | 
			
		||||
		return errors.New("agent: key not found")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Remove removes all identities with the given public key.
 | 
			
		||||
func (r *keyring) Remove(key ssh.PublicKey) error {
 | 
			
		||||
	r.mu.Lock()
 | 
			
		||||
	defer r.mu.Unlock()
 | 
			
		||||
	if r.locked {
 | 
			
		||||
		return errLocked
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r.removeLocked(key.Marshal())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Lock locks the agent. Sign and Remove will fail, and List will return an empty list.
 | 
			
		||||
func (r *keyring) Lock(passphrase []byte) error {
 | 
			
		||||
	r.mu.Lock()
 | 
			
		||||
	defer r.mu.Unlock()
 | 
			
		||||
	if r.locked {
 | 
			
		||||
		return errLocked
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.locked = true
 | 
			
		||||
	r.passphrase = passphrase
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unlock undoes the effect of Lock
 | 
			
		||||
func (r *keyring) Unlock(passphrase []byte) error {
 | 
			
		||||
	r.mu.Lock()
 | 
			
		||||
	defer r.mu.Unlock()
 | 
			
		||||
	if !r.locked {
 | 
			
		||||
		return errors.New("agent: not locked")
 | 
			
		||||
	}
 | 
			
		||||
	if len(passphrase) != len(r.passphrase) || 1 != subtle.ConstantTimeCompare(passphrase, r.passphrase) {
 | 
			
		||||
		return fmt.Errorf("agent: incorrect passphrase")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.locked = false
 | 
			
		||||
	r.passphrase = nil
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// expireKeysLocked removes expired keys from the keyring. If a key was added
 | 
			
		||||
// with a lifetimesecs contraint and seconds >= lifetimesecs seconds have
 | 
			
		||||
// ellapsed, it is removed. The caller *must* be holding the keyring mutex.
 | 
			
		||||
func (r *keyring) expireKeysLocked() {
 | 
			
		||||
	for _, k := range r.keys {
 | 
			
		||||
		if k.expire != nil && time.Now().After(*k.expire) {
 | 
			
		||||
			r.removeLocked(k.signer.PublicKey().Marshal())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// List returns the identities known to the agent.
 | 
			
		||||
func (r *keyring) List() ([]*Key, error) {
 | 
			
		||||
	r.mu.Lock()
 | 
			
		||||
	defer r.mu.Unlock()
 | 
			
		||||
	if r.locked {
 | 
			
		||||
		// section 2.7: locked agents return empty.
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.expireKeysLocked()
 | 
			
		||||
	var ids []*Key
 | 
			
		||||
	for _, k := range r.keys {
 | 
			
		||||
		pub := k.signer.PublicKey()
 | 
			
		||||
		ids = append(ids, &Key{
 | 
			
		||||
			Format:  pub.Type(),
 | 
			
		||||
			Blob:    pub.Marshal(),
 | 
			
		||||
			Comment: k.comment})
 | 
			
		||||
	}
 | 
			
		||||
	return ids, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Insert adds a private key to the keyring. If a certificate
 | 
			
		||||
// is given, that certificate is added as public key. Note that
 | 
			
		||||
// any constraints given are ignored.
 | 
			
		||||
func (r *keyring) Add(key AddedKey) error {
 | 
			
		||||
	r.mu.Lock()
 | 
			
		||||
	defer r.mu.Unlock()
 | 
			
		||||
	if r.locked {
 | 
			
		||||
		return errLocked
 | 
			
		||||
	}
 | 
			
		||||
	signer, err := ssh.NewSignerFromKey(key.PrivateKey)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cert := key.Certificate; cert != nil {
 | 
			
		||||
		signer, err = ssh.NewCertSigner(cert, signer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := privKey{
 | 
			
		||||
		signer:  signer,
 | 
			
		||||
		comment: key.Comment,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if key.LifetimeSecs > 0 {
 | 
			
		||||
		t := time.Now().Add(time.Duration(key.LifetimeSecs) * time.Second)
 | 
			
		||||
		p.expire = &t
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.keys = append(r.keys, p)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sign returns a signature for the data.
 | 
			
		||||
func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
 | 
			
		||||
	r.mu.Lock()
 | 
			
		||||
	defer r.mu.Unlock()
 | 
			
		||||
	if r.locked {
 | 
			
		||||
		return nil, errLocked
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.expireKeysLocked()
 | 
			
		||||
	wanted := key.Marshal()
 | 
			
		||||
	for _, k := range r.keys {
 | 
			
		||||
		if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) {
 | 
			
		||||
			return k.signer.Sign(rand.Reader, data)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, errors.New("not found")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Signers returns signers for all the known keys.
 | 
			
		||||
func (r *keyring) Signers() ([]ssh.Signer, error) {
 | 
			
		||||
	r.mu.Lock()
 | 
			
		||||
	defer r.mu.Unlock()
 | 
			
		||||
	if r.locked {
 | 
			
		||||
		return nil, errLocked
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.expireKeysLocked()
 | 
			
		||||
	s := make([]ssh.Signer, 0, len(r.keys))
 | 
			
		||||
	for _, k := range r.keys {
 | 
			
		||||
		s = append(s, k.signer)
 | 
			
		||||
	}
 | 
			
		||||
	return s, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										523
									
								
								vendor/golang.org/x/crypto/ssh/agent/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										523
									
								
								vendor/golang.org/x/crypto/ssh/agent/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,523 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package agent
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/dsa"
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"math/big"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/ed25519"
 | 
			
		||||
	"golang.org/x/crypto/ssh"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Server wraps an Agent and uses it to implement the agent side of
 | 
			
		||||
// the SSH-agent, wire protocol.
 | 
			
		||||
type server struct {
 | 
			
		||||
	agent Agent
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *server) processRequestBytes(reqData []byte) []byte {
 | 
			
		||||
	rep, err := s.processRequest(reqData)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if err != errLocked {
 | 
			
		||||
			// TODO(hanwen): provide better logging interface?
 | 
			
		||||
			log.Printf("agent %d: %v", reqData[0], err)
 | 
			
		||||
		}
 | 
			
		||||
		return []byte{agentFailure}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err == nil && rep == nil {
 | 
			
		||||
		return []byte{agentSuccess}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ssh.Marshal(rep)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalKey(k *Key) []byte {
 | 
			
		||||
	var record struct {
 | 
			
		||||
		Blob    []byte
 | 
			
		||||
		Comment string
 | 
			
		||||
	}
 | 
			
		||||
	record.Blob = k.Marshal()
 | 
			
		||||
	record.Comment = k.Comment
 | 
			
		||||
 | 
			
		||||
	return ssh.Marshal(&record)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See [PROTOCOL.agent], section 2.5.1.
 | 
			
		||||
const agentV1IdentitiesAnswer = 2
 | 
			
		||||
 | 
			
		||||
type agentV1IdentityMsg struct {
 | 
			
		||||
	Numkeys uint32 `sshtype:"2"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type agentRemoveIdentityMsg struct {
 | 
			
		||||
	KeyBlob []byte `sshtype:"18"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type agentLockMsg struct {
 | 
			
		||||
	Passphrase []byte `sshtype:"22"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type agentUnlockMsg struct {
 | 
			
		||||
	Passphrase []byte `sshtype:"23"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *server) processRequest(data []byte) (interface{}, error) {
 | 
			
		||||
	switch data[0] {
 | 
			
		||||
	case agentRequestV1Identities:
 | 
			
		||||
		return &agentV1IdentityMsg{0}, nil
 | 
			
		||||
 | 
			
		||||
	case agentRemoveAllV1Identities:
 | 
			
		||||
		return nil, nil
 | 
			
		||||
 | 
			
		||||
	case agentRemoveIdentity:
 | 
			
		||||
		var req agentRemoveIdentityMsg
 | 
			
		||||
		if err := ssh.Unmarshal(data, &req); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var wk wireKey
 | 
			
		||||
		if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil, s.agent.Remove(&Key{Format: wk.Format, Blob: req.KeyBlob})
 | 
			
		||||
 | 
			
		||||
	case agentRemoveAllIdentities:
 | 
			
		||||
		return nil, s.agent.RemoveAll()
 | 
			
		||||
 | 
			
		||||
	case agentLock:
 | 
			
		||||
		var req agentLockMsg
 | 
			
		||||
		if err := ssh.Unmarshal(data, &req); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil, s.agent.Lock(req.Passphrase)
 | 
			
		||||
 | 
			
		||||
	case agentUnlock:
 | 
			
		||||
		var req agentUnlockMsg
 | 
			
		||||
		if err := ssh.Unmarshal(data, &req); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		return nil, s.agent.Unlock(req.Passphrase)
 | 
			
		||||
 | 
			
		||||
	case agentSignRequest:
 | 
			
		||||
		var req signRequestAgentMsg
 | 
			
		||||
		if err := ssh.Unmarshal(data, &req); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var wk wireKey
 | 
			
		||||
		if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		k := &Key{
 | 
			
		||||
			Format: wk.Format,
 | 
			
		||||
			Blob:   req.KeyBlob,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sig, err := s.agent.Sign(k, req.Data) //  TODO(hanwen): flags.
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil
 | 
			
		||||
 | 
			
		||||
	case agentRequestIdentities:
 | 
			
		||||
		keys, err := s.agent.List()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rep := identitiesAnswerAgentMsg{
 | 
			
		||||
			NumKeys: uint32(len(keys)),
 | 
			
		||||
		}
 | 
			
		||||
		for _, k := range keys {
 | 
			
		||||
			rep.Keys = append(rep.Keys, marshalKey(k)...)
 | 
			
		||||
		}
 | 
			
		||||
		return rep, nil
 | 
			
		||||
 | 
			
		||||
	case agentAddIDConstrained, agentAddIdentity:
 | 
			
		||||
		return nil, s.insertIdentity(data)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, fmt.Errorf("unknown opcode %d", data[0])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseConstraints(constraints []byte) (lifetimeSecs uint32, confirmBeforeUse bool, extensions []ConstraintExtension, err error) {
 | 
			
		||||
	for len(constraints) != 0 {
 | 
			
		||||
		switch constraints[0] {
 | 
			
		||||
		case agentConstrainLifetime:
 | 
			
		||||
			lifetimeSecs = binary.BigEndian.Uint32(constraints[1:5])
 | 
			
		||||
			constraints = constraints[5:]
 | 
			
		||||
		case agentConstrainConfirm:
 | 
			
		||||
			confirmBeforeUse = true
 | 
			
		||||
			constraints = constraints[1:]
 | 
			
		||||
		case agentConstrainExtension:
 | 
			
		||||
			var msg constrainExtensionAgentMsg
 | 
			
		||||
			if err = ssh.Unmarshal(constraints, &msg); err != nil {
 | 
			
		||||
				return 0, false, nil, err
 | 
			
		||||
			}
 | 
			
		||||
			extensions = append(extensions, ConstraintExtension{
 | 
			
		||||
				ExtensionName:    msg.ExtensionName,
 | 
			
		||||
				ExtensionDetails: msg.ExtensionDetails,
 | 
			
		||||
			})
 | 
			
		||||
			constraints = msg.Rest
 | 
			
		||||
		default:
 | 
			
		||||
			return 0, false, nil, fmt.Errorf("unknown constraint type: %d", constraints[0])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setConstraints(key *AddedKey, constraintBytes []byte) error {
 | 
			
		||||
	lifetimeSecs, confirmBeforeUse, constraintExtensions, err := parseConstraints(constraintBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	key.LifetimeSecs = lifetimeSecs
 | 
			
		||||
	key.ConfirmBeforeUse = confirmBeforeUse
 | 
			
		||||
	key.ConstraintExtensions = constraintExtensions
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseRSAKey(req []byte) (*AddedKey, error) {
 | 
			
		||||
	var k rsaKeyMsg
 | 
			
		||||
	if err := ssh.Unmarshal(req, &k); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if k.E.BitLen() > 30 {
 | 
			
		||||
		return nil, errors.New("agent: RSA public exponent too large")
 | 
			
		||||
	}
 | 
			
		||||
	priv := &rsa.PrivateKey{
 | 
			
		||||
		PublicKey: rsa.PublicKey{
 | 
			
		||||
			E: int(k.E.Int64()),
 | 
			
		||||
			N: k.N,
 | 
			
		||||
		},
 | 
			
		||||
		D:      k.D,
 | 
			
		||||
		Primes: []*big.Int{k.P, k.Q},
 | 
			
		||||
	}
 | 
			
		||||
	priv.Precompute()
 | 
			
		||||
 | 
			
		||||
	addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments}
 | 
			
		||||
	if err := setConstraints(addedKey, k.Constraints); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return addedKey, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseEd25519Key(req []byte) (*AddedKey, error) {
 | 
			
		||||
	var k ed25519KeyMsg
 | 
			
		||||
	if err := ssh.Unmarshal(req, &k); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	priv := ed25519.PrivateKey(k.Priv)
 | 
			
		||||
 | 
			
		||||
	addedKey := &AddedKey{PrivateKey: &priv, Comment: k.Comments}
 | 
			
		||||
	if err := setConstraints(addedKey, k.Constraints); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return addedKey, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseDSAKey(req []byte) (*AddedKey, error) {
 | 
			
		||||
	var k dsaKeyMsg
 | 
			
		||||
	if err := ssh.Unmarshal(req, &k); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	priv := &dsa.PrivateKey{
 | 
			
		||||
		PublicKey: dsa.PublicKey{
 | 
			
		||||
			Parameters: dsa.Parameters{
 | 
			
		||||
				P: k.P,
 | 
			
		||||
				Q: k.Q,
 | 
			
		||||
				G: k.G,
 | 
			
		||||
			},
 | 
			
		||||
			Y: k.Y,
 | 
			
		||||
		},
 | 
			
		||||
		X: k.X,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments}
 | 
			
		||||
	if err := setConstraints(addedKey, k.Constraints); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return addedKey, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unmarshalECDSA(curveName string, keyBytes []byte, privScalar *big.Int) (priv *ecdsa.PrivateKey, err error) {
 | 
			
		||||
	priv = &ecdsa.PrivateKey{
 | 
			
		||||
		D: privScalar,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch curveName {
 | 
			
		||||
	case "nistp256":
 | 
			
		||||
		priv.Curve = elliptic.P256()
 | 
			
		||||
	case "nistp384":
 | 
			
		||||
		priv.Curve = elliptic.P384()
 | 
			
		||||
	case "nistp521":
 | 
			
		||||
		priv.Curve = elliptic.P521()
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("agent: unknown curve %q", curveName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	priv.X, priv.Y = elliptic.Unmarshal(priv.Curve, keyBytes)
 | 
			
		||||
	if priv.X == nil || priv.Y == nil {
 | 
			
		||||
		return nil, errors.New("agent: point not on curve")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return priv, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseEd25519Cert(req []byte) (*AddedKey, error) {
 | 
			
		||||
	var k ed25519CertMsg
 | 
			
		||||
	if err := ssh.Unmarshal(req, &k); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	pubKey, err := ssh.ParsePublicKey(k.CertBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	priv := ed25519.PrivateKey(k.Priv)
 | 
			
		||||
	cert, ok := pubKey.(*ssh.Certificate)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("agent: bad ED25519 certificate")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}
 | 
			
		||||
	if err := setConstraints(addedKey, k.Constraints); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return addedKey, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseECDSAKey(req []byte) (*AddedKey, error) {
 | 
			
		||||
	var k ecdsaKeyMsg
 | 
			
		||||
	if err := ssh.Unmarshal(req, &k); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	priv, err := unmarshalECDSA(k.Curve, k.KeyBytes, k.D)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments}
 | 
			
		||||
	if err := setConstraints(addedKey, k.Constraints); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return addedKey, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseRSACert(req []byte) (*AddedKey, error) {
 | 
			
		||||
	var k rsaCertMsg
 | 
			
		||||
	if err := ssh.Unmarshal(req, &k); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pubKey, err := ssh.ParsePublicKey(k.CertBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cert, ok := pubKey.(*ssh.Certificate)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("agent: bad RSA certificate")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// An RSA publickey as marshaled by rsaPublicKey.Marshal() in keys.go
 | 
			
		||||
	var rsaPub struct {
 | 
			
		||||
		Name string
 | 
			
		||||
		E    *big.Int
 | 
			
		||||
		N    *big.Int
 | 
			
		||||
	}
 | 
			
		||||
	if err := ssh.Unmarshal(cert.Key.Marshal(), &rsaPub); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if rsaPub.E.BitLen() > 30 {
 | 
			
		||||
		return nil, errors.New("agent: RSA public exponent too large")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	priv := rsa.PrivateKey{
 | 
			
		||||
		PublicKey: rsa.PublicKey{
 | 
			
		||||
			E: int(rsaPub.E.Int64()),
 | 
			
		||||
			N: rsaPub.N,
 | 
			
		||||
		},
 | 
			
		||||
		D:      k.D,
 | 
			
		||||
		Primes: []*big.Int{k.Q, k.P},
 | 
			
		||||
	}
 | 
			
		||||
	priv.Precompute()
 | 
			
		||||
 | 
			
		||||
	addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}
 | 
			
		||||
	if err := setConstraints(addedKey, k.Constraints); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return addedKey, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseDSACert(req []byte) (*AddedKey, error) {
 | 
			
		||||
	var k dsaCertMsg
 | 
			
		||||
	if err := ssh.Unmarshal(req, &k); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	pubKey, err := ssh.ParsePublicKey(k.CertBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	cert, ok := pubKey.(*ssh.Certificate)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("agent: bad DSA certificate")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// A DSA publickey as marshaled by dsaPublicKey.Marshal() in keys.go
 | 
			
		||||
	var w struct {
 | 
			
		||||
		Name       string
 | 
			
		||||
		P, Q, G, Y *big.Int
 | 
			
		||||
	}
 | 
			
		||||
	if err := ssh.Unmarshal(cert.Key.Marshal(), &w); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	priv := &dsa.PrivateKey{
 | 
			
		||||
		PublicKey: dsa.PublicKey{
 | 
			
		||||
			Parameters: dsa.Parameters{
 | 
			
		||||
				P: w.P,
 | 
			
		||||
				Q: w.Q,
 | 
			
		||||
				G: w.G,
 | 
			
		||||
			},
 | 
			
		||||
			Y: w.Y,
 | 
			
		||||
		},
 | 
			
		||||
		X: k.X,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}
 | 
			
		||||
	if err := setConstraints(addedKey, k.Constraints); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return addedKey, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseECDSACert(req []byte) (*AddedKey, error) {
 | 
			
		||||
	var k ecdsaCertMsg
 | 
			
		||||
	if err := ssh.Unmarshal(req, &k); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pubKey, err := ssh.ParsePublicKey(k.CertBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	cert, ok := pubKey.(*ssh.Certificate)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("agent: bad ECDSA certificate")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// An ECDSA publickey as marshaled by ecdsaPublicKey.Marshal() in keys.go
 | 
			
		||||
	var ecdsaPub struct {
 | 
			
		||||
		Name string
 | 
			
		||||
		ID   string
 | 
			
		||||
		Key  []byte
 | 
			
		||||
	}
 | 
			
		||||
	if err := ssh.Unmarshal(cert.Key.Marshal(), &ecdsaPub); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	priv, err := unmarshalECDSA(ecdsaPub.ID, ecdsaPub.Key, k.D)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}
 | 
			
		||||
	if err := setConstraints(addedKey, k.Constraints); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return addedKey, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *server) insertIdentity(req []byte) error {
 | 
			
		||||
	var record struct {
 | 
			
		||||
		Type string `sshtype:"17|25"`
 | 
			
		||||
		Rest []byte `ssh:"rest"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := ssh.Unmarshal(req, &record); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var addedKey *AddedKey
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	switch record.Type {
 | 
			
		||||
	case ssh.KeyAlgoRSA:
 | 
			
		||||
		addedKey, err = parseRSAKey(req)
 | 
			
		||||
	case ssh.KeyAlgoDSA:
 | 
			
		||||
		addedKey, err = parseDSAKey(req)
 | 
			
		||||
	case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521:
 | 
			
		||||
		addedKey, err = parseECDSAKey(req)
 | 
			
		||||
	case ssh.KeyAlgoED25519:
 | 
			
		||||
		addedKey, err = parseEd25519Key(req)
 | 
			
		||||
	case ssh.CertAlgoRSAv01:
 | 
			
		||||
		addedKey, err = parseRSACert(req)
 | 
			
		||||
	case ssh.CertAlgoDSAv01:
 | 
			
		||||
		addedKey, err = parseDSACert(req)
 | 
			
		||||
	case ssh.CertAlgoECDSA256v01, ssh.CertAlgoECDSA384v01, ssh.CertAlgoECDSA521v01:
 | 
			
		||||
		addedKey, err = parseECDSACert(req)
 | 
			
		||||
	case ssh.CertAlgoED25519v01:
 | 
			
		||||
		addedKey, err = parseEd25519Cert(req)
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("agent: not implemented: %q", record.Type)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return s.agent.Add(*addedKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServeAgent serves the agent protocol on the given connection. It
 | 
			
		||||
// returns when an I/O error occurs.
 | 
			
		||||
func ServeAgent(agent Agent, c io.ReadWriter) error {
 | 
			
		||||
	s := &server{agent}
 | 
			
		||||
 | 
			
		||||
	var length [4]byte
 | 
			
		||||
	for {
 | 
			
		||||
		if _, err := io.ReadFull(c, length[:]); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		l := binary.BigEndian.Uint32(length[:])
 | 
			
		||||
		if l > maxAgentResponseBytes {
 | 
			
		||||
			// We also cap requests.
 | 
			
		||||
			return fmt.Errorf("agent: request too large: %d", l)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		req := make([]byte, l)
 | 
			
		||||
		if _, err := io.ReadFull(c, req); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		repData := s.processRequestBytes(req)
 | 
			
		||||
		if len(repData) > maxAgentResponseBytes {
 | 
			
		||||
			return fmt.Errorf("agent: reply too large: %d bytes", len(repData))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		binary.BigEndian.PutUint32(length[:], uint32(len(repData)))
 | 
			
		||||
		if _, err := c.Write(length[:]); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if _, err := c.Write(repData); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										97
									
								
								vendor/golang.org/x/crypto/ssh/buffer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								vendor/golang.org/x/crypto/ssh/buffer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// buffer provides a linked list buffer for data exchange
 | 
			
		||||
// between producer and consumer. Theoretically the buffer is
 | 
			
		||||
// of unlimited capacity as it does no allocation of its own.
 | 
			
		||||
type buffer struct {
 | 
			
		||||
	// protects concurrent access to head, tail and closed
 | 
			
		||||
	*sync.Cond
 | 
			
		||||
 | 
			
		||||
	head *element // the buffer that will be read first
 | 
			
		||||
	tail *element // the buffer that will be read last
 | 
			
		||||
 | 
			
		||||
	closed bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An element represents a single link in a linked list.
 | 
			
		||||
type element struct {
 | 
			
		||||
	buf  []byte
 | 
			
		||||
	next *element
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newBuffer returns an empty buffer that is not closed.
 | 
			
		||||
func newBuffer() *buffer {
 | 
			
		||||
	e := new(element)
 | 
			
		||||
	b := &buffer{
 | 
			
		||||
		Cond: newCond(),
 | 
			
		||||
		head: e,
 | 
			
		||||
		tail: e,
 | 
			
		||||
	}
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// write makes buf available for Read to receive.
 | 
			
		||||
// buf must not be modified after the call to write.
 | 
			
		||||
func (b *buffer) write(buf []byte) {
 | 
			
		||||
	b.Cond.L.Lock()
 | 
			
		||||
	e := &element{buf: buf}
 | 
			
		||||
	b.tail.next = e
 | 
			
		||||
	b.tail = e
 | 
			
		||||
	b.Cond.Signal()
 | 
			
		||||
	b.Cond.L.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// eof closes the buffer. Reads from the buffer once all
 | 
			
		||||
// the data has been consumed will receive io.EOF.
 | 
			
		||||
func (b *buffer) eof() {
 | 
			
		||||
	b.Cond.L.Lock()
 | 
			
		||||
	b.closed = true
 | 
			
		||||
	b.Cond.Signal()
 | 
			
		||||
	b.Cond.L.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read reads data from the internal buffer in buf.  Reads will block
 | 
			
		||||
// if no data is available, or until the buffer is closed.
 | 
			
		||||
func (b *buffer) Read(buf []byte) (n int, err error) {
 | 
			
		||||
	b.Cond.L.Lock()
 | 
			
		||||
	defer b.Cond.L.Unlock()
 | 
			
		||||
 | 
			
		||||
	for len(buf) > 0 {
 | 
			
		||||
		// if there is data in b.head, copy it
 | 
			
		||||
		if len(b.head.buf) > 0 {
 | 
			
		||||
			r := copy(buf, b.head.buf)
 | 
			
		||||
			buf, b.head.buf = buf[r:], b.head.buf[r:]
 | 
			
		||||
			n += r
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		// if there is a next buffer, make it the head
 | 
			
		||||
		if len(b.head.buf) == 0 && b.head != b.tail {
 | 
			
		||||
			b.head = b.head.next
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// if at least one byte has been copied, return
 | 
			
		||||
		if n > 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// if nothing was read, and there is nothing outstanding
 | 
			
		||||
		// check to see if the buffer is closed.
 | 
			
		||||
		if b.closed {
 | 
			
		||||
			err = io.EOF
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		// out of buffers, wait for producer
 | 
			
		||||
		b.Cond.Wait()
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										519
									
								
								vendor/golang.org/x/crypto/ssh/certs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										519
									
								
								vendor/golang.org/x/crypto/ssh/certs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,519 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// These constants from [PROTOCOL.certkeys] represent the algorithm names
 | 
			
		||||
// for certificate types supported by this package.
 | 
			
		||||
const (
 | 
			
		||||
	CertAlgoRSAv01      = "ssh-rsa-cert-v01@openssh.com"
 | 
			
		||||
	CertAlgoDSAv01      = "ssh-dss-cert-v01@openssh.com"
 | 
			
		||||
	CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
 | 
			
		||||
	CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
 | 
			
		||||
	CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
 | 
			
		||||
	CertAlgoED25519v01  = "ssh-ed25519-cert-v01@openssh.com"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Certificate types distinguish between host and user
 | 
			
		||||
// certificates. The values can be set in the CertType field of
 | 
			
		||||
// Certificate.
 | 
			
		||||
const (
 | 
			
		||||
	UserCert = 1
 | 
			
		||||
	HostCert = 2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Signature represents a cryptographic signature.
 | 
			
		||||
type Signature struct {
 | 
			
		||||
	Format string
 | 
			
		||||
	Blob   []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that
 | 
			
		||||
// a certificate does not expire.
 | 
			
		||||
const CertTimeInfinity = 1<<64 - 1
 | 
			
		||||
 | 
			
		||||
// An Certificate represents an OpenSSH certificate as defined in
 | 
			
		||||
// [PROTOCOL.certkeys]?rev=1.8.
 | 
			
		||||
type Certificate struct {
 | 
			
		||||
	Nonce           []byte
 | 
			
		||||
	Key             PublicKey
 | 
			
		||||
	Serial          uint64
 | 
			
		||||
	CertType        uint32
 | 
			
		||||
	KeyId           string
 | 
			
		||||
	ValidPrincipals []string
 | 
			
		||||
	ValidAfter      uint64
 | 
			
		||||
	ValidBefore     uint64
 | 
			
		||||
	Permissions
 | 
			
		||||
	Reserved     []byte
 | 
			
		||||
	SignatureKey PublicKey
 | 
			
		||||
	Signature    *Signature
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// genericCertData holds the key-independent part of the certificate data.
 | 
			
		||||
// Overall, certificates contain an nonce, public key fields and
 | 
			
		||||
// key-independent fields.
 | 
			
		||||
type genericCertData struct {
 | 
			
		||||
	Serial          uint64
 | 
			
		||||
	CertType        uint32
 | 
			
		||||
	KeyId           string
 | 
			
		||||
	ValidPrincipals []byte
 | 
			
		||||
	ValidAfter      uint64
 | 
			
		||||
	ValidBefore     uint64
 | 
			
		||||
	CriticalOptions []byte
 | 
			
		||||
	Extensions      []byte
 | 
			
		||||
	Reserved        []byte
 | 
			
		||||
	SignatureKey    []byte
 | 
			
		||||
	Signature       []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalStringList(namelist []string) []byte {
 | 
			
		||||
	var to []byte
 | 
			
		||||
	for _, name := range namelist {
 | 
			
		||||
		s := struct{ N string }{name}
 | 
			
		||||
		to = append(to, Marshal(&s)...)
 | 
			
		||||
	}
 | 
			
		||||
	return to
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type optionsTuple struct {
 | 
			
		||||
	Key   string
 | 
			
		||||
	Value []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type optionsTupleValue struct {
 | 
			
		||||
	Value string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// serialize a map of critical options or extensions
 | 
			
		||||
// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation,
 | 
			
		||||
// we need two length prefixes for a non-empty string value
 | 
			
		||||
func marshalTuples(tups map[string]string) []byte {
 | 
			
		||||
	keys := make([]string, 0, len(tups))
 | 
			
		||||
	for key := range tups {
 | 
			
		||||
		keys = append(keys, key)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(keys)
 | 
			
		||||
 | 
			
		||||
	var ret []byte
 | 
			
		||||
	for _, key := range keys {
 | 
			
		||||
		s := optionsTuple{Key: key}
 | 
			
		||||
		if value := tups[key]; len(value) > 0 {
 | 
			
		||||
			s.Value = Marshal(&optionsTupleValue{value})
 | 
			
		||||
		}
 | 
			
		||||
		ret = append(ret, Marshal(&s)...)
 | 
			
		||||
	}
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation,
 | 
			
		||||
// we need two length prefixes for a non-empty option value
 | 
			
		||||
func parseTuples(in []byte) (map[string]string, error) {
 | 
			
		||||
	tups := map[string]string{}
 | 
			
		||||
	var lastKey string
 | 
			
		||||
	var haveLastKey bool
 | 
			
		||||
 | 
			
		||||
	for len(in) > 0 {
 | 
			
		||||
		var key, val, extra []byte
 | 
			
		||||
		var ok bool
 | 
			
		||||
 | 
			
		||||
		if key, in, ok = parseString(in); !ok {
 | 
			
		||||
			return nil, errShortRead
 | 
			
		||||
		}
 | 
			
		||||
		keyStr := string(key)
 | 
			
		||||
		// according to [PROTOCOL.certkeys], the names must be in
 | 
			
		||||
		// lexical order.
 | 
			
		||||
		if haveLastKey && keyStr <= lastKey {
 | 
			
		||||
			return nil, fmt.Errorf("ssh: certificate options are not in lexical order")
 | 
			
		||||
		}
 | 
			
		||||
		lastKey, haveLastKey = keyStr, true
 | 
			
		||||
		// the next field is a data field, which if non-empty has a string embedded
 | 
			
		||||
		if val, in, ok = parseString(in); !ok {
 | 
			
		||||
			return nil, errShortRead
 | 
			
		||||
		}
 | 
			
		||||
		if len(val) > 0 {
 | 
			
		||||
			val, extra, ok = parseString(val)
 | 
			
		||||
			if !ok {
 | 
			
		||||
				return nil, errShortRead
 | 
			
		||||
			}
 | 
			
		||||
			if len(extra) > 0 {
 | 
			
		||||
				return nil, fmt.Errorf("ssh: unexpected trailing data after certificate option value")
 | 
			
		||||
			}
 | 
			
		||||
			tups[keyStr] = string(val)
 | 
			
		||||
		} else {
 | 
			
		||||
			tups[keyStr] = ""
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return tups, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseCert(in []byte, privAlgo string) (*Certificate, error) {
 | 
			
		||||
	nonce, rest, ok := parseString(in)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errShortRead
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	key, rest, err := parsePubKey(rest, privAlgo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var g genericCertData
 | 
			
		||||
	if err := Unmarshal(rest, &g); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := &Certificate{
 | 
			
		||||
		Nonce:       nonce,
 | 
			
		||||
		Key:         key,
 | 
			
		||||
		Serial:      g.Serial,
 | 
			
		||||
		CertType:    g.CertType,
 | 
			
		||||
		KeyId:       g.KeyId,
 | 
			
		||||
		ValidAfter:  g.ValidAfter,
 | 
			
		||||
		ValidBefore: g.ValidBefore,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for principals := g.ValidPrincipals; len(principals) > 0; {
 | 
			
		||||
		principal, rest, ok := parseString(principals)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return nil, errShortRead
 | 
			
		||||
		}
 | 
			
		||||
		c.ValidPrincipals = append(c.ValidPrincipals, string(principal))
 | 
			
		||||
		principals = rest
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.CriticalOptions, err = parseTuples(g.CriticalOptions)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	c.Extensions, err = parseTuples(g.Extensions)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	c.Reserved = g.Reserved
 | 
			
		||||
	k, err := ParsePublicKey(g.SignatureKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.SignatureKey = k
 | 
			
		||||
	c.Signature, rest, ok = parseSignatureBody(g.Signature)
 | 
			
		||||
	if !ok || len(rest) > 0 {
 | 
			
		||||
		return nil, errors.New("ssh: signature parse error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type openSSHCertSigner struct {
 | 
			
		||||
	pub    *Certificate
 | 
			
		||||
	signer Signer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCertSigner returns a Signer that signs with the given Certificate, whose
 | 
			
		||||
// private key is held by signer. It returns an error if the public key in cert
 | 
			
		||||
// doesn't match the key used by signer.
 | 
			
		||||
func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) {
 | 
			
		||||
	if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 {
 | 
			
		||||
		return nil, errors.New("ssh: signer and cert have different public key")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &openSSHCertSigner{cert, signer}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
 | 
			
		||||
	return s.signer.Sign(rand, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *openSSHCertSigner) PublicKey() PublicKey {
 | 
			
		||||
	return s.pub
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const sourceAddressCriticalOption = "source-address"
 | 
			
		||||
 | 
			
		||||
// CertChecker does the work of verifying a certificate. Its methods
 | 
			
		||||
// can be plugged into ClientConfig.HostKeyCallback and
 | 
			
		||||
// ServerConfig.PublicKeyCallback. For the CertChecker to work,
 | 
			
		||||
// minimally, the IsAuthority callback should be set.
 | 
			
		||||
type CertChecker struct {
 | 
			
		||||
	// SupportedCriticalOptions lists the CriticalOptions that the
 | 
			
		||||
	// server application layer understands. These are only used
 | 
			
		||||
	// for user certificates.
 | 
			
		||||
	SupportedCriticalOptions []string
 | 
			
		||||
 | 
			
		||||
	// IsUserAuthority should return true if the key is recognized as an
 | 
			
		||||
	// authority for the given user certificate. This allows for
 | 
			
		||||
	// certificates to be signed by other certificates. This must be set
 | 
			
		||||
	// if this CertChecker will be checking user certificates.
 | 
			
		||||
	IsUserAuthority func(auth PublicKey) bool
 | 
			
		||||
 | 
			
		||||
	// IsHostAuthority should report whether the key is recognized as
 | 
			
		||||
	// an authority for this host. This allows for certificates to be
 | 
			
		||||
	// signed by other keys, and for those other keys to only be valid
 | 
			
		||||
	// signers for particular hostnames. This must be set if this
 | 
			
		||||
	// CertChecker will be checking host certificates.
 | 
			
		||||
	IsHostAuthority func(auth PublicKey, address string) bool
 | 
			
		||||
 | 
			
		||||
	// Clock is used for verifying time stamps. If nil, time.Now
 | 
			
		||||
	// is used.
 | 
			
		||||
	Clock func() time.Time
 | 
			
		||||
 | 
			
		||||
	// UserKeyFallback is called when CertChecker.Authenticate encounters a
 | 
			
		||||
	// public key that is not a certificate. It must implement validation
 | 
			
		||||
	// of user keys or else, if nil, all such keys are rejected.
 | 
			
		||||
	UserKeyFallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
 | 
			
		||||
 | 
			
		||||
	// HostKeyFallback is called when CertChecker.CheckHostKey encounters a
 | 
			
		||||
	// public key that is not a certificate. It must implement host key
 | 
			
		||||
	// validation or else, if nil, all such keys are rejected.
 | 
			
		||||
	HostKeyFallback HostKeyCallback
 | 
			
		||||
 | 
			
		||||
	// IsRevoked is called for each certificate so that revocation checking
 | 
			
		||||
	// can be implemented. It should return true if the given certificate
 | 
			
		||||
	// is revoked and false otherwise. If nil, no certificates are
 | 
			
		||||
	// considered to have been revoked.
 | 
			
		||||
	IsRevoked func(cert *Certificate) bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckHostKey checks a host key certificate. This method can be
 | 
			
		||||
// plugged into ClientConfig.HostKeyCallback.
 | 
			
		||||
func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) error {
 | 
			
		||||
	cert, ok := key.(*Certificate)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		if c.HostKeyFallback != nil {
 | 
			
		||||
			return c.HostKeyFallback(addr, remote, key)
 | 
			
		||||
		}
 | 
			
		||||
		return errors.New("ssh: non-certificate host key")
 | 
			
		||||
	}
 | 
			
		||||
	if cert.CertType != HostCert {
 | 
			
		||||
		return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType)
 | 
			
		||||
	}
 | 
			
		||||
	if !c.IsHostAuthority(cert.SignatureKey, addr) {
 | 
			
		||||
		return fmt.Errorf("ssh: no authorities for hostname: %v", addr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hostname, _, err := net.SplitHostPort(addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Pass hostname only as principal for host certificates (consistent with OpenSSH)
 | 
			
		||||
	return c.CheckCert(hostname, cert)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Authenticate checks a user certificate. Authenticate can be used as
 | 
			
		||||
// a value for ServerConfig.PublicKeyCallback.
 | 
			
		||||
func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error) {
 | 
			
		||||
	cert, ok := pubKey.(*Certificate)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		if c.UserKeyFallback != nil {
 | 
			
		||||
			return c.UserKeyFallback(conn, pubKey)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, errors.New("ssh: normal key pairs not accepted")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cert.CertType != UserCert {
 | 
			
		||||
		return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType)
 | 
			
		||||
	}
 | 
			
		||||
	if !c.IsUserAuthority(cert.SignatureKey) {
 | 
			
		||||
		return nil, fmt.Errorf("ssh: certificate signed by unrecognized authority")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := c.CheckCert(conn.User(), cert); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &cert.Permissions, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and
 | 
			
		||||
// the signature of the certificate.
 | 
			
		||||
func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
 | 
			
		||||
	if c.IsRevoked != nil && c.IsRevoked(cert) {
 | 
			
		||||
		return fmt.Errorf("ssh: certicate serial %d revoked", cert.Serial)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for opt := range cert.CriticalOptions {
 | 
			
		||||
		// sourceAddressCriticalOption will be enforced by
 | 
			
		||||
		// serverAuthenticate
 | 
			
		||||
		if opt == sourceAddressCriticalOption {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		found := false
 | 
			
		||||
		for _, supp := range c.SupportedCriticalOptions {
 | 
			
		||||
			if supp == opt {
 | 
			
		||||
				found = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !found {
 | 
			
		||||
			return fmt.Errorf("ssh: unsupported critical option %q in certificate", opt)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(cert.ValidPrincipals) > 0 {
 | 
			
		||||
		// By default, certs are valid for all users/hosts.
 | 
			
		||||
		found := false
 | 
			
		||||
		for _, p := range cert.ValidPrincipals {
 | 
			
		||||
			if p == principal {
 | 
			
		||||
				found = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !found {
 | 
			
		||||
			return fmt.Errorf("ssh: principal %q not in the set of valid principals for given certificate: %q", principal, cert.ValidPrincipals)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clock := c.Clock
 | 
			
		||||
	if clock == nil {
 | 
			
		||||
		clock = time.Now
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	unixNow := clock().Unix()
 | 
			
		||||
	if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) {
 | 
			
		||||
		return fmt.Errorf("ssh: cert is not yet valid")
 | 
			
		||||
	}
 | 
			
		||||
	if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) {
 | 
			
		||||
		return fmt.Errorf("ssh: cert has expired")
 | 
			
		||||
	}
 | 
			
		||||
	if err := cert.SignatureKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil {
 | 
			
		||||
		return fmt.Errorf("ssh: certificate signature does not verify")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SignCert sets c.SignatureKey to the authority's public key and stores a
 | 
			
		||||
// Signature, by authority, in the certificate.
 | 
			
		||||
func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
 | 
			
		||||
	c.Nonce = make([]byte, 32)
 | 
			
		||||
	if _, err := io.ReadFull(rand, c.Nonce); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	c.SignatureKey = authority.PublicKey()
 | 
			
		||||
 | 
			
		||||
	sig, err := authority.Sign(rand, c.bytesForSigning())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	c.Signature = sig
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var certAlgoNames = map[string]string{
 | 
			
		||||
	KeyAlgoRSA:      CertAlgoRSAv01,
 | 
			
		||||
	KeyAlgoDSA:      CertAlgoDSAv01,
 | 
			
		||||
	KeyAlgoECDSA256: CertAlgoECDSA256v01,
 | 
			
		||||
	KeyAlgoECDSA384: CertAlgoECDSA384v01,
 | 
			
		||||
	KeyAlgoECDSA521: CertAlgoECDSA521v01,
 | 
			
		||||
	KeyAlgoED25519:  CertAlgoED25519v01,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// certToPrivAlgo returns the underlying algorithm for a certificate algorithm.
 | 
			
		||||
// Panics if a non-certificate algorithm is passed.
 | 
			
		||||
func certToPrivAlgo(algo string) string {
 | 
			
		||||
	for privAlgo, pubAlgo := range certAlgoNames {
 | 
			
		||||
		if pubAlgo == algo {
 | 
			
		||||
			return privAlgo
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	panic("unknown cert algorithm")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cert *Certificate) bytesForSigning() []byte {
 | 
			
		||||
	c2 := *cert
 | 
			
		||||
	c2.Signature = nil
 | 
			
		||||
	out := c2.Marshal()
 | 
			
		||||
	// Drop trailing signature length.
 | 
			
		||||
	return out[:len(out)-4]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Marshal serializes c into OpenSSH's wire format. It is part of the
 | 
			
		||||
// PublicKey interface.
 | 
			
		||||
func (c *Certificate) Marshal() []byte {
 | 
			
		||||
	generic := genericCertData{
 | 
			
		||||
		Serial:          c.Serial,
 | 
			
		||||
		CertType:        c.CertType,
 | 
			
		||||
		KeyId:           c.KeyId,
 | 
			
		||||
		ValidPrincipals: marshalStringList(c.ValidPrincipals),
 | 
			
		||||
		ValidAfter:      uint64(c.ValidAfter),
 | 
			
		||||
		ValidBefore:     uint64(c.ValidBefore),
 | 
			
		||||
		CriticalOptions: marshalTuples(c.CriticalOptions),
 | 
			
		||||
		Extensions:      marshalTuples(c.Extensions),
 | 
			
		||||
		Reserved:        c.Reserved,
 | 
			
		||||
		SignatureKey:    c.SignatureKey.Marshal(),
 | 
			
		||||
	}
 | 
			
		||||
	if c.Signature != nil {
 | 
			
		||||
		generic.Signature = Marshal(c.Signature)
 | 
			
		||||
	}
 | 
			
		||||
	genericBytes := Marshal(&generic)
 | 
			
		||||
	keyBytes := c.Key.Marshal()
 | 
			
		||||
	_, keyBytes, _ = parseString(keyBytes)
 | 
			
		||||
	prefix := Marshal(&struct {
 | 
			
		||||
		Name  string
 | 
			
		||||
		Nonce []byte
 | 
			
		||||
		Key   []byte `ssh:"rest"`
 | 
			
		||||
	}{c.Type(), c.Nonce, keyBytes})
 | 
			
		||||
 | 
			
		||||
	result := make([]byte, 0, len(prefix)+len(genericBytes))
 | 
			
		||||
	result = append(result, prefix...)
 | 
			
		||||
	result = append(result, genericBytes...)
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Type returns the key name. It is part of the PublicKey interface.
 | 
			
		||||
func (c *Certificate) Type() string {
 | 
			
		||||
	algo, ok := certAlgoNames[c.Key.Type()]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		panic("unknown cert key type " + c.Key.Type())
 | 
			
		||||
	}
 | 
			
		||||
	return algo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Verify verifies a signature against the certificate's public
 | 
			
		||||
// key. It is part of the PublicKey interface.
 | 
			
		||||
func (c *Certificate) Verify(data []byte, sig *Signature) error {
 | 
			
		||||
	return c.Key.Verify(data, sig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) {
 | 
			
		||||
	format, in, ok := parseString(in)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out = &Signature{
 | 
			
		||||
		Format: string(format),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if out.Blob, in, ok = parseString(in); !ok {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return out, in, ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseSignature(in []byte) (out *Signature, rest []byte, ok bool) {
 | 
			
		||||
	sigBytes, rest, ok := parseString(in)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out, trailing, ok := parseSignatureBody(sigBytes)
 | 
			
		||||
	if !ok || len(trailing) > 0 {
 | 
			
		||||
		return nil, nil, false
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										633
									
								
								vendor/golang.org/x/crypto/ssh/channel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										633
									
								
								vendor/golang.org/x/crypto/ssh/channel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,633 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	minPacketLength = 9
 | 
			
		||||
	// channelMaxPacket contains the maximum number of bytes that will be
 | 
			
		||||
	// sent in a single packet. As per RFC 4253, section 6.1, 32k is also
 | 
			
		||||
	// the minimum.
 | 
			
		||||
	channelMaxPacket = 1 << 15
 | 
			
		||||
	// We follow OpenSSH here.
 | 
			
		||||
	channelWindowSize = 64 * channelMaxPacket
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewChannel represents an incoming request to a channel. It must either be
 | 
			
		||||
// accepted for use by calling Accept, or rejected by calling Reject.
 | 
			
		||||
type NewChannel interface {
 | 
			
		||||
	// Accept accepts the channel creation request. It returns the Channel
 | 
			
		||||
	// and a Go channel containing SSH requests. The Go channel must be
 | 
			
		||||
	// serviced otherwise the Channel will hang.
 | 
			
		||||
	Accept() (Channel, <-chan *Request, error)
 | 
			
		||||
 | 
			
		||||
	// Reject rejects the channel creation request. After calling
 | 
			
		||||
	// this, no other methods on the Channel may be called.
 | 
			
		||||
	Reject(reason RejectionReason, message string) error
 | 
			
		||||
 | 
			
		||||
	// ChannelType returns the type of the channel, as supplied by the
 | 
			
		||||
	// client.
 | 
			
		||||
	ChannelType() string
 | 
			
		||||
 | 
			
		||||
	// ExtraData returns the arbitrary payload for this channel, as supplied
 | 
			
		||||
	// by the client. This data is specific to the channel type.
 | 
			
		||||
	ExtraData() []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A Channel is an ordered, reliable, flow-controlled, duplex stream
 | 
			
		||||
// that is multiplexed over an SSH connection.
 | 
			
		||||
type Channel interface {
 | 
			
		||||
	// Read reads up to len(data) bytes from the channel.
 | 
			
		||||
	Read(data []byte) (int, error)
 | 
			
		||||
 | 
			
		||||
	// Write writes len(data) bytes to the channel.
 | 
			
		||||
	Write(data []byte) (int, error)
 | 
			
		||||
 | 
			
		||||
	// Close signals end of channel use. No data may be sent after this
 | 
			
		||||
	// call.
 | 
			
		||||
	Close() error
 | 
			
		||||
 | 
			
		||||
	// CloseWrite signals the end of sending in-band
 | 
			
		||||
	// data. Requests may still be sent, and the other side may
 | 
			
		||||
	// still send data
 | 
			
		||||
	CloseWrite() error
 | 
			
		||||
 | 
			
		||||
	// SendRequest sends a channel request.  If wantReply is true,
 | 
			
		||||
	// it will wait for a reply and return the result as a
 | 
			
		||||
	// boolean, otherwise the return value will be false. Channel
 | 
			
		||||
	// requests are out-of-band messages so they may be sent even
 | 
			
		||||
	// if the data stream is closed or blocked by flow control.
 | 
			
		||||
	// If the channel is closed before a reply is returned, io.EOF
 | 
			
		||||
	// is returned.
 | 
			
		||||
	SendRequest(name string, wantReply bool, payload []byte) (bool, error)
 | 
			
		||||
 | 
			
		||||
	// Stderr returns an io.ReadWriter that writes to this channel
 | 
			
		||||
	// with the extended data type set to stderr. Stderr may
 | 
			
		||||
	// safely be read and written from a different goroutine than
 | 
			
		||||
	// Read and Write respectively.
 | 
			
		||||
	Stderr() io.ReadWriter
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Request is a request sent outside of the normal stream of
 | 
			
		||||
// data. Requests can either be specific to an SSH channel, or they
 | 
			
		||||
// can be global.
 | 
			
		||||
type Request struct {
 | 
			
		||||
	Type      string
 | 
			
		||||
	WantReply bool
 | 
			
		||||
	Payload   []byte
 | 
			
		||||
 | 
			
		||||
	ch  *channel
 | 
			
		||||
	mux *mux
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reply sends a response to a request. It must be called for all requests
 | 
			
		||||
// where WantReply is true and is a no-op otherwise. The payload argument is
 | 
			
		||||
// ignored for replies to channel-specific requests.
 | 
			
		||||
func (r *Request) Reply(ok bool, payload []byte) error {
 | 
			
		||||
	if !r.WantReply {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r.ch == nil {
 | 
			
		||||
		return r.mux.ackRequest(ok, payload)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r.ch.ackRequest(ok)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RejectionReason is an enumeration used when rejecting channel creation
 | 
			
		||||
// requests. See RFC 4254, section 5.1.
 | 
			
		||||
type RejectionReason uint32
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	Prohibited RejectionReason = iota + 1
 | 
			
		||||
	ConnectionFailed
 | 
			
		||||
	UnknownChannelType
 | 
			
		||||
	ResourceShortage
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// String converts the rejection reason to human readable form.
 | 
			
		||||
func (r RejectionReason) String() string {
 | 
			
		||||
	switch r {
 | 
			
		||||
	case Prohibited:
 | 
			
		||||
		return "administratively prohibited"
 | 
			
		||||
	case ConnectionFailed:
 | 
			
		||||
		return "connect failed"
 | 
			
		||||
	case UnknownChannelType:
 | 
			
		||||
		return "unknown channel type"
 | 
			
		||||
	case ResourceShortage:
 | 
			
		||||
		return "resource shortage"
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("unknown reason %d", int(r))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func min(a uint32, b int) uint32 {
 | 
			
		||||
	if a < uint32(b) {
 | 
			
		||||
		return a
 | 
			
		||||
	}
 | 
			
		||||
	return uint32(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type channelDirection uint8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	channelInbound channelDirection = iota
 | 
			
		||||
	channelOutbound
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// channel is an implementation of the Channel interface that works
 | 
			
		||||
// with the mux class.
 | 
			
		||||
type channel struct {
 | 
			
		||||
	// R/O after creation
 | 
			
		||||
	chanType          string
 | 
			
		||||
	extraData         []byte
 | 
			
		||||
	localId, remoteId uint32
 | 
			
		||||
 | 
			
		||||
	// maxIncomingPayload and maxRemotePayload are the maximum
 | 
			
		||||
	// payload sizes of normal and extended data packets for
 | 
			
		||||
	// receiving and sending, respectively. The wire packet will
 | 
			
		||||
	// be 9 or 13 bytes larger (excluding encryption overhead).
 | 
			
		||||
	maxIncomingPayload uint32
 | 
			
		||||
	maxRemotePayload   uint32
 | 
			
		||||
 | 
			
		||||
	mux *mux
 | 
			
		||||
 | 
			
		||||
	// decided is set to true if an accept or reject message has been sent
 | 
			
		||||
	// (for outbound channels) or received (for inbound channels).
 | 
			
		||||
	decided bool
 | 
			
		||||
 | 
			
		||||
	// direction contains either channelOutbound, for channels created
 | 
			
		||||
	// locally, or channelInbound, for channels created by the peer.
 | 
			
		||||
	direction channelDirection
 | 
			
		||||
 | 
			
		||||
	// Pending internal channel messages.
 | 
			
		||||
	msg chan interface{}
 | 
			
		||||
 | 
			
		||||
	// Since requests have no ID, there can be only one request
 | 
			
		||||
	// with WantReply=true outstanding.  This lock is held by a
 | 
			
		||||
	// goroutine that has such an outgoing request pending.
 | 
			
		||||
	sentRequestMu sync.Mutex
 | 
			
		||||
 | 
			
		||||
	incomingRequests chan *Request
 | 
			
		||||
 | 
			
		||||
	sentEOF bool
 | 
			
		||||
 | 
			
		||||
	// thread-safe data
 | 
			
		||||
	remoteWin  window
 | 
			
		||||
	pending    *buffer
 | 
			
		||||
	extPending *buffer
 | 
			
		||||
 | 
			
		||||
	// windowMu protects myWindow, the flow-control window.
 | 
			
		||||
	windowMu sync.Mutex
 | 
			
		||||
	myWindow uint32
 | 
			
		||||
 | 
			
		||||
	// writeMu serializes calls to mux.conn.writePacket() and
 | 
			
		||||
	// protects sentClose and packetPool. This mutex must be
 | 
			
		||||
	// different from windowMu, as writePacket can block if there
 | 
			
		||||
	// is a key exchange pending.
 | 
			
		||||
	writeMu   sync.Mutex
 | 
			
		||||
	sentClose bool
 | 
			
		||||
 | 
			
		||||
	// packetPool has a buffer for each extended channel ID to
 | 
			
		||||
	// save allocations during writes.
 | 
			
		||||
	packetPool map[uint32][]byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writePacket sends a packet. If the packet is a channel close, it updates
 | 
			
		||||
// sentClose. This method takes the lock c.writeMu.
 | 
			
		||||
func (ch *channel) writePacket(packet []byte) error {
 | 
			
		||||
	ch.writeMu.Lock()
 | 
			
		||||
	if ch.sentClose {
 | 
			
		||||
		ch.writeMu.Unlock()
 | 
			
		||||
		return io.EOF
 | 
			
		||||
	}
 | 
			
		||||
	ch.sentClose = (packet[0] == msgChannelClose)
 | 
			
		||||
	err := ch.mux.conn.writePacket(packet)
 | 
			
		||||
	ch.writeMu.Unlock()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) sendMessage(msg interface{}) error {
 | 
			
		||||
	if debugMux {
 | 
			
		||||
		log.Printf("send(%d): %#v", ch.mux.chanList.offset, msg)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := Marshal(msg)
 | 
			
		||||
	binary.BigEndian.PutUint32(p[1:], ch.remoteId)
 | 
			
		||||
	return ch.writePacket(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteExtended writes data to a specific extended stream. These streams are
 | 
			
		||||
// used, for example, for stderr.
 | 
			
		||||
func (ch *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err error) {
 | 
			
		||||
	if ch.sentEOF {
 | 
			
		||||
		return 0, io.EOF
 | 
			
		||||
	}
 | 
			
		||||
	// 1 byte message type, 4 bytes remoteId, 4 bytes data length
 | 
			
		||||
	opCode := byte(msgChannelData)
 | 
			
		||||
	headerLength := uint32(9)
 | 
			
		||||
	if extendedCode > 0 {
 | 
			
		||||
		headerLength += 4
 | 
			
		||||
		opCode = msgChannelExtendedData
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ch.writeMu.Lock()
 | 
			
		||||
	packet := ch.packetPool[extendedCode]
 | 
			
		||||
	// We don't remove the buffer from packetPool, so
 | 
			
		||||
	// WriteExtended calls from different goroutines will be
 | 
			
		||||
	// flagged as errors by the race detector.
 | 
			
		||||
	ch.writeMu.Unlock()
 | 
			
		||||
 | 
			
		||||
	for len(data) > 0 {
 | 
			
		||||
		space := min(ch.maxRemotePayload, len(data))
 | 
			
		||||
		if space, err = ch.remoteWin.reserve(space); err != nil {
 | 
			
		||||
			return n, err
 | 
			
		||||
		}
 | 
			
		||||
		if want := headerLength + space; uint32(cap(packet)) < want {
 | 
			
		||||
			packet = make([]byte, want)
 | 
			
		||||
		} else {
 | 
			
		||||
			packet = packet[:want]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		todo := data[:space]
 | 
			
		||||
 | 
			
		||||
		packet[0] = opCode
 | 
			
		||||
		binary.BigEndian.PutUint32(packet[1:], ch.remoteId)
 | 
			
		||||
		if extendedCode > 0 {
 | 
			
		||||
			binary.BigEndian.PutUint32(packet[5:], uint32(extendedCode))
 | 
			
		||||
		}
 | 
			
		||||
		binary.BigEndian.PutUint32(packet[headerLength-4:], uint32(len(todo)))
 | 
			
		||||
		copy(packet[headerLength:], todo)
 | 
			
		||||
		if err = ch.writePacket(packet); err != nil {
 | 
			
		||||
			return n, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		n += len(todo)
 | 
			
		||||
		data = data[len(todo):]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ch.writeMu.Lock()
 | 
			
		||||
	ch.packetPool[extendedCode] = packet
 | 
			
		||||
	ch.writeMu.Unlock()
 | 
			
		||||
 | 
			
		||||
	return n, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) handleData(packet []byte) error {
 | 
			
		||||
	headerLen := 9
 | 
			
		||||
	isExtendedData := packet[0] == msgChannelExtendedData
 | 
			
		||||
	if isExtendedData {
 | 
			
		||||
		headerLen = 13
 | 
			
		||||
	}
 | 
			
		||||
	if len(packet) < headerLen {
 | 
			
		||||
		// malformed data packet
 | 
			
		||||
		return parseError(packet[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var extended uint32
 | 
			
		||||
	if isExtendedData {
 | 
			
		||||
		extended = binary.BigEndian.Uint32(packet[5:])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	length := binary.BigEndian.Uint32(packet[headerLen-4 : headerLen])
 | 
			
		||||
	if length == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if length > ch.maxIncomingPayload {
 | 
			
		||||
		// TODO(hanwen): should send Disconnect?
 | 
			
		||||
		return errors.New("ssh: incoming packet exceeds maximum payload size")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data := packet[headerLen:]
 | 
			
		||||
	if length != uint32(len(data)) {
 | 
			
		||||
		return errors.New("ssh: wrong packet length")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ch.windowMu.Lock()
 | 
			
		||||
	if ch.myWindow < length {
 | 
			
		||||
		ch.windowMu.Unlock()
 | 
			
		||||
		// TODO(hanwen): should send Disconnect with reason?
 | 
			
		||||
		return errors.New("ssh: remote side wrote too much")
 | 
			
		||||
	}
 | 
			
		||||
	ch.myWindow -= length
 | 
			
		||||
	ch.windowMu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if extended == 1 {
 | 
			
		||||
		ch.extPending.write(data)
 | 
			
		||||
	} else if extended > 0 {
 | 
			
		||||
		// discard other extended data.
 | 
			
		||||
	} else {
 | 
			
		||||
		ch.pending.write(data)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *channel) adjustWindow(n uint32) error {
 | 
			
		||||
	c.windowMu.Lock()
 | 
			
		||||
	// Since myWindow is managed on our side, and can never exceed
 | 
			
		||||
	// the initial window setting, we don't worry about overflow.
 | 
			
		||||
	c.myWindow += uint32(n)
 | 
			
		||||
	c.windowMu.Unlock()
 | 
			
		||||
	return c.sendMessage(windowAdjustMsg{
 | 
			
		||||
		AdditionalBytes: uint32(n),
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) {
 | 
			
		||||
	switch extended {
 | 
			
		||||
	case 1:
 | 
			
		||||
		n, err = c.extPending.Read(data)
 | 
			
		||||
	case 0:
 | 
			
		||||
		n, err = c.pending.Read(data)
 | 
			
		||||
	default:
 | 
			
		||||
		return 0, fmt.Errorf("ssh: extended code %d unimplemented", extended)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if n > 0 {
 | 
			
		||||
		err = c.adjustWindow(uint32(n))
 | 
			
		||||
		// sendWindowAdjust can return io.EOF if the remote
 | 
			
		||||
		// peer has closed the connection, however we want to
 | 
			
		||||
		// defer forwarding io.EOF to the caller of Read until
 | 
			
		||||
		// the buffer has been drained.
 | 
			
		||||
		if n > 0 && err == io.EOF {
 | 
			
		||||
			err = nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return n, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *channel) close() {
 | 
			
		||||
	c.pending.eof()
 | 
			
		||||
	c.extPending.eof()
 | 
			
		||||
	close(c.msg)
 | 
			
		||||
	close(c.incomingRequests)
 | 
			
		||||
	c.writeMu.Lock()
 | 
			
		||||
	// This is not necessary for a normal channel teardown, but if
 | 
			
		||||
	// there was another error, it is.
 | 
			
		||||
	c.sentClose = true
 | 
			
		||||
	c.writeMu.Unlock()
 | 
			
		||||
	// Unblock writers.
 | 
			
		||||
	c.remoteWin.close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// responseMessageReceived is called when a success or failure message is
 | 
			
		||||
// received on a channel to check that such a message is reasonable for the
 | 
			
		||||
// given channel.
 | 
			
		||||
func (ch *channel) responseMessageReceived() error {
 | 
			
		||||
	if ch.direction == channelInbound {
 | 
			
		||||
		return errors.New("ssh: channel response message received on inbound channel")
 | 
			
		||||
	}
 | 
			
		||||
	if ch.decided {
 | 
			
		||||
		return errors.New("ssh: duplicate response received for channel")
 | 
			
		||||
	}
 | 
			
		||||
	ch.decided = true
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) handlePacket(packet []byte) error {
 | 
			
		||||
	switch packet[0] {
 | 
			
		||||
	case msgChannelData, msgChannelExtendedData:
 | 
			
		||||
		return ch.handleData(packet)
 | 
			
		||||
	case msgChannelClose:
 | 
			
		||||
		ch.sendMessage(channelCloseMsg{PeersID: ch.remoteId})
 | 
			
		||||
		ch.mux.chanList.remove(ch.localId)
 | 
			
		||||
		ch.close()
 | 
			
		||||
		return nil
 | 
			
		||||
	case msgChannelEOF:
 | 
			
		||||
		// RFC 4254 is mute on how EOF affects dataExt messages but
 | 
			
		||||
		// it is logical to signal EOF at the same time.
 | 
			
		||||
		ch.extPending.eof()
 | 
			
		||||
		ch.pending.eof()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decoded, err := decode(packet)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch msg := decoded.(type) {
 | 
			
		||||
	case *channelOpenFailureMsg:
 | 
			
		||||
		if err := ch.responseMessageReceived(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		ch.mux.chanList.remove(msg.PeersID)
 | 
			
		||||
		ch.msg <- msg
 | 
			
		||||
	case *channelOpenConfirmMsg:
 | 
			
		||||
		if err := ch.responseMessageReceived(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 {
 | 
			
		||||
			return fmt.Errorf("ssh: invalid MaxPacketSize %d from peer", msg.MaxPacketSize)
 | 
			
		||||
		}
 | 
			
		||||
		ch.remoteId = msg.MyID
 | 
			
		||||
		ch.maxRemotePayload = msg.MaxPacketSize
 | 
			
		||||
		ch.remoteWin.add(msg.MyWindow)
 | 
			
		||||
		ch.msg <- msg
 | 
			
		||||
	case *windowAdjustMsg:
 | 
			
		||||
		if !ch.remoteWin.add(msg.AdditionalBytes) {
 | 
			
		||||
			return fmt.Errorf("ssh: invalid window update for %d bytes", msg.AdditionalBytes)
 | 
			
		||||
		}
 | 
			
		||||
	case *channelRequestMsg:
 | 
			
		||||
		req := Request{
 | 
			
		||||
			Type:      msg.Request,
 | 
			
		||||
			WantReply: msg.WantReply,
 | 
			
		||||
			Payload:   msg.RequestSpecificData,
 | 
			
		||||
			ch:        ch,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ch.incomingRequests <- &req
 | 
			
		||||
	default:
 | 
			
		||||
		ch.msg <- msg
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *mux) newChannel(chanType string, direction channelDirection, extraData []byte) *channel {
 | 
			
		||||
	ch := &channel{
 | 
			
		||||
		remoteWin:        window{Cond: newCond()},
 | 
			
		||||
		myWindow:         channelWindowSize,
 | 
			
		||||
		pending:          newBuffer(),
 | 
			
		||||
		extPending:       newBuffer(),
 | 
			
		||||
		direction:        direction,
 | 
			
		||||
		incomingRequests: make(chan *Request, chanSize),
 | 
			
		||||
		msg:              make(chan interface{}, chanSize),
 | 
			
		||||
		chanType:         chanType,
 | 
			
		||||
		extraData:        extraData,
 | 
			
		||||
		mux:              m,
 | 
			
		||||
		packetPool:       make(map[uint32][]byte),
 | 
			
		||||
	}
 | 
			
		||||
	ch.localId = m.chanList.add(ch)
 | 
			
		||||
	return ch
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errUndecided = errors.New("ssh: must Accept or Reject channel")
 | 
			
		||||
var errDecidedAlready = errors.New("ssh: can call Accept or Reject only once")
 | 
			
		||||
 | 
			
		||||
type extChannel struct {
 | 
			
		||||
	code uint32
 | 
			
		||||
	ch   *channel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *extChannel) Write(data []byte) (n int, err error) {
 | 
			
		||||
	return e.ch.WriteExtended(data, e.code)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *extChannel) Read(data []byte) (n int, err error) {
 | 
			
		||||
	return e.ch.ReadExtended(data, e.code)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) Accept() (Channel, <-chan *Request, error) {
 | 
			
		||||
	if ch.decided {
 | 
			
		||||
		return nil, nil, errDecidedAlready
 | 
			
		||||
	}
 | 
			
		||||
	ch.maxIncomingPayload = channelMaxPacket
 | 
			
		||||
	confirm := channelOpenConfirmMsg{
 | 
			
		||||
		PeersID:       ch.remoteId,
 | 
			
		||||
		MyID:          ch.localId,
 | 
			
		||||
		MyWindow:      ch.myWindow,
 | 
			
		||||
		MaxPacketSize: ch.maxIncomingPayload,
 | 
			
		||||
	}
 | 
			
		||||
	ch.decided = true
 | 
			
		||||
	if err := ch.sendMessage(confirm); err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ch, ch.incomingRequests, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) Reject(reason RejectionReason, message string) error {
 | 
			
		||||
	if ch.decided {
 | 
			
		||||
		return errDecidedAlready
 | 
			
		||||
	}
 | 
			
		||||
	reject := channelOpenFailureMsg{
 | 
			
		||||
		PeersID:  ch.remoteId,
 | 
			
		||||
		Reason:   reason,
 | 
			
		||||
		Message:  message,
 | 
			
		||||
		Language: "en",
 | 
			
		||||
	}
 | 
			
		||||
	ch.decided = true
 | 
			
		||||
	return ch.sendMessage(reject)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) Read(data []byte) (int, error) {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return 0, errUndecided
 | 
			
		||||
	}
 | 
			
		||||
	return ch.ReadExtended(data, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) Write(data []byte) (int, error) {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return 0, errUndecided
 | 
			
		||||
	}
 | 
			
		||||
	return ch.WriteExtended(data, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) CloseWrite() error {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return errUndecided
 | 
			
		||||
	}
 | 
			
		||||
	ch.sentEOF = true
 | 
			
		||||
	return ch.sendMessage(channelEOFMsg{
 | 
			
		||||
		PeersID: ch.remoteId})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) Close() error {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return errUndecided
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ch.sendMessage(channelCloseMsg{
 | 
			
		||||
		PeersID: ch.remoteId})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extended returns an io.ReadWriter that sends and receives data on the given,
 | 
			
		||||
// SSH extended stream. Such streams are used, for example, for stderr.
 | 
			
		||||
func (ch *channel) Extended(code uint32) io.ReadWriter {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return &extChannel{code, ch}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) Stderr() io.ReadWriter {
 | 
			
		||||
	return ch.Extended(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return false, errUndecided
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if wantReply {
 | 
			
		||||
		ch.sentRequestMu.Lock()
 | 
			
		||||
		defer ch.sentRequestMu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg := channelRequestMsg{
 | 
			
		||||
		PeersID:             ch.remoteId,
 | 
			
		||||
		Request:             name,
 | 
			
		||||
		WantReply:           wantReply,
 | 
			
		||||
		RequestSpecificData: payload,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := ch.sendMessage(msg); err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if wantReply {
 | 
			
		||||
		m, ok := (<-ch.msg)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return false, io.EOF
 | 
			
		||||
		}
 | 
			
		||||
		switch m.(type) {
 | 
			
		||||
		case *channelRequestFailureMsg:
 | 
			
		||||
			return false, nil
 | 
			
		||||
		case *channelRequestSuccessMsg:
 | 
			
		||||
			return true, nil
 | 
			
		||||
		default:
 | 
			
		||||
			return false, fmt.Errorf("ssh: unexpected response to channel request: %#v", m)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ackRequest either sends an ack or nack to the channel request.
 | 
			
		||||
func (ch *channel) ackRequest(ok bool) error {
 | 
			
		||||
	if !ch.decided {
 | 
			
		||||
		return errUndecided
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var msg interface{}
 | 
			
		||||
	if !ok {
 | 
			
		||||
		msg = channelRequestFailureMsg{
 | 
			
		||||
			PeersID: ch.remoteId,
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		msg = channelRequestSuccessMsg{
 | 
			
		||||
			PeersID: ch.remoteId,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ch.sendMessage(msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) ChannelType() string {
 | 
			
		||||
	return ch.chanType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ch *channel) ExtraData() []byte {
 | 
			
		||||
	return ch.extraData
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										629
									
								
								vendor/golang.org/x/crypto/ssh/cipher.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										629
									
								
								vendor/golang.org/x/crypto/ssh/cipher.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,629 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/aes"
 | 
			
		||||
	"crypto/cipher"
 | 
			
		||||
	"crypto/des"
 | 
			
		||||
	"crypto/rc4"
 | 
			
		||||
	"crypto/subtle"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"hash"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher.
 | 
			
		||||
 | 
			
		||||
	// RFC 4253 section 6.1 defines a minimum packet size of 32768 that implementations
 | 
			
		||||
	// MUST be able to process (plus a few more kilobytes for padding and mac). The RFC
 | 
			
		||||
	// indicates implementations SHOULD be able to handle larger packet sizes, but then
 | 
			
		||||
	// waffles on about reasonable limits.
 | 
			
		||||
	//
 | 
			
		||||
	// OpenSSH caps their maxPacket at 256kB so we choose to do
 | 
			
		||||
	// the same. maxPacket is also used to ensure that uint32
 | 
			
		||||
	// length fields do not overflow, so it should remain well
 | 
			
		||||
	// below 4G.
 | 
			
		||||
	maxPacket = 256 * 1024
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// noneCipher implements cipher.Stream and provides no encryption. It is used
 | 
			
		||||
// by the transport before the first key-exchange.
 | 
			
		||||
type noneCipher struct{}
 | 
			
		||||
 | 
			
		||||
func (c noneCipher) XORKeyStream(dst, src []byte) {
 | 
			
		||||
	copy(dst, src)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newAESCTR(key, iv []byte) (cipher.Stream, error) {
 | 
			
		||||
	c, err := aes.NewCipher(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return cipher.NewCTR(c, iv), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newRC4(key, iv []byte) (cipher.Stream, error) {
 | 
			
		||||
	return rc4.NewCipher(key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type streamCipherMode struct {
 | 
			
		||||
	keySize    int
 | 
			
		||||
	ivSize     int
 | 
			
		||||
	skip       int
 | 
			
		||||
	createFunc func(key, iv []byte) (cipher.Stream, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *streamCipherMode) createStream(key, iv []byte) (cipher.Stream, error) {
 | 
			
		||||
	if len(key) < c.keySize {
 | 
			
		||||
		panic("ssh: key length too small for cipher")
 | 
			
		||||
	}
 | 
			
		||||
	if len(iv) < c.ivSize {
 | 
			
		||||
		panic("ssh: iv too small for cipher")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stream, err := c.createFunc(key[:c.keySize], iv[:c.ivSize])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var streamDump []byte
 | 
			
		||||
	if c.skip > 0 {
 | 
			
		||||
		streamDump = make([]byte, 512)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for remainingToDump := c.skip; remainingToDump > 0; {
 | 
			
		||||
		dumpThisTime := remainingToDump
 | 
			
		||||
		if dumpThisTime > len(streamDump) {
 | 
			
		||||
			dumpThisTime = len(streamDump)
 | 
			
		||||
		}
 | 
			
		||||
		stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime])
 | 
			
		||||
		remainingToDump -= dumpThisTime
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return stream, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// cipherModes documents properties of supported ciphers. Ciphers not included
 | 
			
		||||
// are not supported and will not be negotiated, even if explicitly requested in
 | 
			
		||||
// ClientConfig.Crypto.Ciphers.
 | 
			
		||||
var cipherModes = map[string]*streamCipherMode{
 | 
			
		||||
	// Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms
 | 
			
		||||
	// are defined in the order specified in the RFC.
 | 
			
		||||
	"aes128-ctr": {16, aes.BlockSize, 0, newAESCTR},
 | 
			
		||||
	"aes192-ctr": {24, aes.BlockSize, 0, newAESCTR},
 | 
			
		||||
	"aes256-ctr": {32, aes.BlockSize, 0, newAESCTR},
 | 
			
		||||
 | 
			
		||||
	// Ciphers from RFC4345, which introduces security-improved arcfour ciphers.
 | 
			
		||||
	// They are defined in the order specified in the RFC.
 | 
			
		||||
	"arcfour128": {16, 0, 1536, newRC4},
 | 
			
		||||
	"arcfour256": {32, 0, 1536, newRC4},
 | 
			
		||||
 | 
			
		||||
	// Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol.
 | 
			
		||||
	// Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and
 | 
			
		||||
	// RC4) has problems with weak keys, and should be used with caution."
 | 
			
		||||
	// RFC4345 introduces improved versions of Arcfour.
 | 
			
		||||
	"arcfour": {16, 0, 0, newRC4},
 | 
			
		||||
 | 
			
		||||
	// AES-GCM is not a stream cipher, so it is constructed with a
 | 
			
		||||
	// special case. If we add any more non-stream ciphers, we
 | 
			
		||||
	// should invest a cleaner way to do this.
 | 
			
		||||
	gcmCipherID: {16, 12, 0, nil},
 | 
			
		||||
 | 
			
		||||
	// CBC mode is insecure and so is not included in the default config.
 | 
			
		||||
	// (See http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf). If absolutely
 | 
			
		||||
	// needed, it's possible to specify a custom Config to enable it.
 | 
			
		||||
	// You should expect that an active attacker can recover plaintext if
 | 
			
		||||
	// you do.
 | 
			
		||||
	aes128cbcID: {16, aes.BlockSize, 0, nil},
 | 
			
		||||
 | 
			
		||||
	// 3des-cbc is insecure and is disabled by default.
 | 
			
		||||
	tripledescbcID: {24, des.BlockSize, 0, nil},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// prefixLen is the length of the packet prefix that contains the packet length
 | 
			
		||||
// and number of padding bytes.
 | 
			
		||||
const prefixLen = 5
 | 
			
		||||
 | 
			
		||||
// streamPacketCipher is a packetCipher using a stream cipher.
 | 
			
		||||
type streamPacketCipher struct {
 | 
			
		||||
	mac    hash.Hash
 | 
			
		||||
	cipher cipher.Stream
 | 
			
		||||
	etm    bool
 | 
			
		||||
 | 
			
		||||
	// The following members are to avoid per-packet allocations.
 | 
			
		||||
	prefix      [prefixLen]byte
 | 
			
		||||
	seqNumBytes [4]byte
 | 
			
		||||
	padding     [2 * packetSizeMultiple]byte
 | 
			
		||||
	packetData  []byte
 | 
			
		||||
	macResult   []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readPacket reads and decrypt a single packet from the reader argument.
 | 
			
		||||
func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
 | 
			
		||||
	if _, err := io.ReadFull(r, s.prefix[:]); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var encryptedPaddingLength [1]byte
 | 
			
		||||
	if s.mac != nil && s.etm {
 | 
			
		||||
		copy(encryptedPaddingLength[:], s.prefix[4:5])
 | 
			
		||||
		s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5])
 | 
			
		||||
	} else {
 | 
			
		||||
		s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	length := binary.BigEndian.Uint32(s.prefix[0:4])
 | 
			
		||||
	paddingLength := uint32(s.prefix[4])
 | 
			
		||||
 | 
			
		||||
	var macSize uint32
 | 
			
		||||
	if s.mac != nil {
 | 
			
		||||
		s.mac.Reset()
 | 
			
		||||
		binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum)
 | 
			
		||||
		s.mac.Write(s.seqNumBytes[:])
 | 
			
		||||
		if s.etm {
 | 
			
		||||
			s.mac.Write(s.prefix[:4])
 | 
			
		||||
			s.mac.Write(encryptedPaddingLength[:])
 | 
			
		||||
		} else {
 | 
			
		||||
			s.mac.Write(s.prefix[:])
 | 
			
		||||
		}
 | 
			
		||||
		macSize = uint32(s.mac.Size())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if length <= paddingLength+1 {
 | 
			
		||||
		return nil, errors.New("ssh: invalid packet length, packet too small")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if length > maxPacket {
 | 
			
		||||
		return nil, errors.New("ssh: invalid packet length, packet too large")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// the maxPacket check above ensures that length-1+macSize
 | 
			
		||||
	// does not overflow.
 | 
			
		||||
	if uint32(cap(s.packetData)) < length-1+macSize {
 | 
			
		||||
		s.packetData = make([]byte, length-1+macSize)
 | 
			
		||||
	} else {
 | 
			
		||||
		s.packetData = s.packetData[:length-1+macSize]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := io.ReadFull(r, s.packetData); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	mac := s.packetData[length-1:]
 | 
			
		||||
	data := s.packetData[:length-1]
 | 
			
		||||
 | 
			
		||||
	if s.mac != nil && s.etm {
 | 
			
		||||
		s.mac.Write(data)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.cipher.XORKeyStream(data, data)
 | 
			
		||||
 | 
			
		||||
	if s.mac != nil {
 | 
			
		||||
		if !s.etm {
 | 
			
		||||
			s.mac.Write(data)
 | 
			
		||||
		}
 | 
			
		||||
		s.macResult = s.mac.Sum(s.macResult[:0])
 | 
			
		||||
		if subtle.ConstantTimeCompare(s.macResult, mac) != 1 {
 | 
			
		||||
			return nil, errors.New("ssh: MAC failure")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s.packetData[:length-paddingLength-1], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writePacket encrypts and sends a packet of data to the writer argument
 | 
			
		||||
func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
 | 
			
		||||
	if len(packet) > maxPacket {
 | 
			
		||||
		return errors.New("ssh: packet too large")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aadlen := 0
 | 
			
		||||
	if s.mac != nil && s.etm {
 | 
			
		||||
		// packet length is not encrypted for EtM modes
 | 
			
		||||
		aadlen = 4
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	paddingLength := packetSizeMultiple - (prefixLen+len(packet)-aadlen)%packetSizeMultiple
 | 
			
		||||
	if paddingLength < 4 {
 | 
			
		||||
		paddingLength += packetSizeMultiple
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	length := len(packet) + 1 + paddingLength
 | 
			
		||||
	binary.BigEndian.PutUint32(s.prefix[:], uint32(length))
 | 
			
		||||
	s.prefix[4] = byte(paddingLength)
 | 
			
		||||
	padding := s.padding[:paddingLength]
 | 
			
		||||
	if _, err := io.ReadFull(rand, padding); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if s.mac != nil {
 | 
			
		||||
		s.mac.Reset()
 | 
			
		||||
		binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum)
 | 
			
		||||
		s.mac.Write(s.seqNumBytes[:])
 | 
			
		||||
 | 
			
		||||
		if s.etm {
 | 
			
		||||
			// For EtM algorithms, the packet length must stay unencrypted,
 | 
			
		||||
			// but the following data (padding length) must be encrypted
 | 
			
		||||
			s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5])
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.mac.Write(s.prefix[:])
 | 
			
		||||
 | 
			
		||||
		if !s.etm {
 | 
			
		||||
			// For non-EtM algorithms, the algorithm is applied on unencrypted data
 | 
			
		||||
			s.mac.Write(packet)
 | 
			
		||||
			s.mac.Write(padding)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !(s.mac != nil && s.etm) {
 | 
			
		||||
		// For EtM algorithms, the padding length has already been encrypted
 | 
			
		||||
		// and the packet length must remain unencrypted
 | 
			
		||||
		s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.cipher.XORKeyStream(packet, packet)
 | 
			
		||||
	s.cipher.XORKeyStream(padding, padding)
 | 
			
		||||
 | 
			
		||||
	if s.mac != nil && s.etm {
 | 
			
		||||
		// For EtM algorithms, packet and padding must be encrypted
 | 
			
		||||
		s.mac.Write(packet)
 | 
			
		||||
		s.mac.Write(padding)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := w.Write(s.prefix[:]); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := w.Write(packet); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := w.Write(padding); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if s.mac != nil {
 | 
			
		||||
		s.macResult = s.mac.Sum(s.macResult[:0])
 | 
			
		||||
		if _, err := w.Write(s.macResult); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type gcmCipher struct {
 | 
			
		||||
	aead   cipher.AEAD
 | 
			
		||||
	prefix [4]byte
 | 
			
		||||
	iv     []byte
 | 
			
		||||
	buf    []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newGCMCipher(iv, key []byte) (packetCipher, error) {
 | 
			
		||||
	c, err := aes.NewCipher(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aead, err := cipher.NewGCM(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &gcmCipher{
 | 
			
		||||
		aead: aead,
 | 
			
		||||
		iv:   iv,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const gcmTagSize = 16
 | 
			
		||||
 | 
			
		||||
func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
 | 
			
		||||
	// Pad out to multiple of 16 bytes. This is different from the
 | 
			
		||||
	// stream cipher because that encrypts the length too.
 | 
			
		||||
	padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple)
 | 
			
		||||
	if padding < 4 {
 | 
			
		||||
		padding += packetSizeMultiple
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	length := uint32(len(packet) + int(padding) + 1)
 | 
			
		||||
	binary.BigEndian.PutUint32(c.prefix[:], length)
 | 
			
		||||
	if _, err := w.Write(c.prefix[:]); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cap(c.buf) < int(length) {
 | 
			
		||||
		c.buf = make([]byte, length)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.buf = c.buf[:length]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.buf[0] = padding
 | 
			
		||||
	copy(c.buf[1:], packet)
 | 
			
		||||
	if _, err := io.ReadFull(rand, c.buf[1+len(packet):]); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	c.buf = c.aead.Seal(c.buf[:0], c.iv, c.buf, c.prefix[:])
 | 
			
		||||
	if _, err := w.Write(c.buf); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	c.incIV()
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *gcmCipher) incIV() {
 | 
			
		||||
	for i := 4 + 7; i >= 4; i-- {
 | 
			
		||||
		c.iv[i]++
 | 
			
		||||
		if c.iv[i] != 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
 | 
			
		||||
	if _, err := io.ReadFull(r, c.prefix[:]); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	length := binary.BigEndian.Uint32(c.prefix[:])
 | 
			
		||||
	if length > maxPacket {
 | 
			
		||||
		return nil, errors.New("ssh: max packet length exceeded")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cap(c.buf) < int(length+gcmTagSize) {
 | 
			
		||||
		c.buf = make([]byte, length+gcmTagSize)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.buf = c.buf[:length+gcmTagSize]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := io.ReadFull(r, c.buf); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	plain, err := c.aead.Open(c.buf[:0], c.iv, c.buf, c.prefix[:])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	c.incIV()
 | 
			
		||||
 | 
			
		||||
	padding := plain[0]
 | 
			
		||||
	if padding < 4 {
 | 
			
		||||
		// padding is a byte, so it automatically satisfies
 | 
			
		||||
		// the maximum size, which is 255.
 | 
			
		||||
		return nil, fmt.Errorf("ssh: illegal padding %d", padding)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if int(padding+1) >= len(plain) {
 | 
			
		||||
		return nil, fmt.Errorf("ssh: padding %d too large", padding)
 | 
			
		||||
	}
 | 
			
		||||
	plain = plain[1 : length-uint32(padding)]
 | 
			
		||||
	return plain, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// cbcCipher implements aes128-cbc cipher defined in RFC 4253 section 6.1
 | 
			
		||||
type cbcCipher struct {
 | 
			
		||||
	mac       hash.Hash
 | 
			
		||||
	macSize   uint32
 | 
			
		||||
	decrypter cipher.BlockMode
 | 
			
		||||
	encrypter cipher.BlockMode
 | 
			
		||||
 | 
			
		||||
	// The following members are to avoid per-packet allocations.
 | 
			
		||||
	seqNumBytes [4]byte
 | 
			
		||||
	packetData  []byte
 | 
			
		||||
	macResult   []byte
 | 
			
		||||
 | 
			
		||||
	// Amount of data we should still read to hide which
 | 
			
		||||
	// verification error triggered.
 | 
			
		||||
	oracleCamouflage uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
 | 
			
		||||
	cbc := &cbcCipher{
 | 
			
		||||
		mac:        macModes[algs.MAC].new(macKey),
 | 
			
		||||
		decrypter:  cipher.NewCBCDecrypter(c, iv),
 | 
			
		||||
		encrypter:  cipher.NewCBCEncrypter(c, iv),
 | 
			
		||||
		packetData: make([]byte, 1024),
 | 
			
		||||
	}
 | 
			
		||||
	if cbc.mac != nil {
 | 
			
		||||
		cbc.macSize = uint32(cbc.mac.Size())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cbc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
 | 
			
		||||
	c, err := aes.NewCipher(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cbc, err := newCBCCipher(c, iv, key, macKey, algs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cbc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newTripleDESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
 | 
			
		||||
	c, err := des.NewTripleDESCipher(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cbc, err := newCBCCipher(c, iv, key, macKey, algs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cbc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func maxUInt32(a, b int) uint32 {
 | 
			
		||||
	if a > b {
 | 
			
		||||
		return uint32(a)
 | 
			
		||||
	}
 | 
			
		||||
	return uint32(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	cbcMinPacketSizeMultiple = 8
 | 
			
		||||
	cbcMinPacketSize         = 16
 | 
			
		||||
	cbcMinPaddingSize        = 4
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// cbcError represents a verification error that may leak information.
 | 
			
		||||
type cbcError string
 | 
			
		||||
 | 
			
		||||
func (e cbcError) Error() string { return string(e) }
 | 
			
		||||
 | 
			
		||||
func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
 | 
			
		||||
	p, err := c.readPacketLeaky(seqNum, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if _, ok := err.(cbcError); ok {
 | 
			
		||||
			// Verification error: read a fixed amount of
 | 
			
		||||
			// data, to make distinguishing between
 | 
			
		||||
			// failing MAC and failing length check more
 | 
			
		||||
			// difficult.
 | 
			
		||||
			io.CopyN(ioutil.Discard, r, int64(c.oracleCamouflage))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return p, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) {
 | 
			
		||||
	blockSize := c.decrypter.BlockSize()
 | 
			
		||||
 | 
			
		||||
	// Read the header, which will include some of the subsequent data in the
 | 
			
		||||
	// case of block ciphers - this is copied back to the payload later.
 | 
			
		||||
	// How many bytes of payload/padding will be read with this first read.
 | 
			
		||||
	firstBlockLength := uint32((prefixLen + blockSize - 1) / blockSize * blockSize)
 | 
			
		||||
	firstBlock := c.packetData[:firstBlockLength]
 | 
			
		||||
	if _, err := io.ReadFull(r, firstBlock); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.oracleCamouflage = maxPacket + 4 + c.macSize - firstBlockLength
 | 
			
		||||
 | 
			
		||||
	c.decrypter.CryptBlocks(firstBlock, firstBlock)
 | 
			
		||||
	length := binary.BigEndian.Uint32(firstBlock[:4])
 | 
			
		||||
	if length > maxPacket {
 | 
			
		||||
		return nil, cbcError("ssh: packet too large")
 | 
			
		||||
	}
 | 
			
		||||
	if length+4 < maxUInt32(cbcMinPacketSize, blockSize) {
 | 
			
		||||
		// The minimum size of a packet is 16 (or the cipher block size, whichever
 | 
			
		||||
		// is larger) bytes.
 | 
			
		||||
		return nil, cbcError("ssh: packet too small")
 | 
			
		||||
	}
 | 
			
		||||
	// The length of the packet (including the length field but not the MAC) must
 | 
			
		||||
	// be a multiple of the block size or 8, whichever is larger.
 | 
			
		||||
	if (length+4)%maxUInt32(cbcMinPacketSizeMultiple, blockSize) != 0 {
 | 
			
		||||
		return nil, cbcError("ssh: invalid packet length multiple")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	paddingLength := uint32(firstBlock[4])
 | 
			
		||||
	if paddingLength < cbcMinPaddingSize || length <= paddingLength+1 {
 | 
			
		||||
		return nil, cbcError("ssh: invalid packet length")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Positions within the c.packetData buffer:
 | 
			
		||||
	macStart := 4 + length
 | 
			
		||||
	paddingStart := macStart - paddingLength
 | 
			
		||||
 | 
			
		||||
	// Entire packet size, starting before length, ending at end of mac.
 | 
			
		||||
	entirePacketSize := macStart + c.macSize
 | 
			
		||||
 | 
			
		||||
	// Ensure c.packetData is large enough for the entire packet data.
 | 
			
		||||
	if uint32(cap(c.packetData)) < entirePacketSize {
 | 
			
		||||
		// Still need to upsize and copy, but this should be rare at runtime, only
 | 
			
		||||
		// on upsizing the packetData buffer.
 | 
			
		||||
		c.packetData = make([]byte, entirePacketSize)
 | 
			
		||||
		copy(c.packetData, firstBlock)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.packetData = c.packetData[:entirePacketSize]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	n, err := io.ReadFull(r, c.packetData[firstBlockLength:])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	c.oracleCamouflage -= uint32(n)
 | 
			
		||||
 | 
			
		||||
	remainingCrypted := c.packetData[firstBlockLength:macStart]
 | 
			
		||||
	c.decrypter.CryptBlocks(remainingCrypted, remainingCrypted)
 | 
			
		||||
 | 
			
		||||
	mac := c.packetData[macStart:]
 | 
			
		||||
	if c.mac != nil {
 | 
			
		||||
		c.mac.Reset()
 | 
			
		||||
		binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum)
 | 
			
		||||
		c.mac.Write(c.seqNumBytes[:])
 | 
			
		||||
		c.mac.Write(c.packetData[:macStart])
 | 
			
		||||
		c.macResult = c.mac.Sum(c.macResult[:0])
 | 
			
		||||
		if subtle.ConstantTimeCompare(c.macResult, mac) != 1 {
 | 
			
		||||
			return nil, cbcError("ssh: MAC failure")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.packetData[prefixLen:paddingStart], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
 | 
			
		||||
	effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize())
 | 
			
		||||
 | 
			
		||||
	// Length of encrypted portion of the packet (header, payload, padding).
 | 
			
		||||
	// Enforce minimum padding and packet size.
 | 
			
		||||
	encLength := maxUInt32(prefixLen+len(packet)+cbcMinPaddingSize, cbcMinPaddingSize)
 | 
			
		||||
	// Enforce block size.
 | 
			
		||||
	encLength = (encLength + effectiveBlockSize - 1) / effectiveBlockSize * effectiveBlockSize
 | 
			
		||||
 | 
			
		||||
	length := encLength - 4
 | 
			
		||||
	paddingLength := int(length) - (1 + len(packet))
 | 
			
		||||
 | 
			
		||||
	// Overall buffer contains: header, payload, padding, mac.
 | 
			
		||||
	// Space for the MAC is reserved in the capacity but not the slice length.
 | 
			
		||||
	bufferSize := encLength + c.macSize
 | 
			
		||||
	if uint32(cap(c.packetData)) < bufferSize {
 | 
			
		||||
		c.packetData = make([]byte, encLength, bufferSize)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.packetData = c.packetData[:encLength]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := c.packetData
 | 
			
		||||
 | 
			
		||||
	// Packet header.
 | 
			
		||||
	binary.BigEndian.PutUint32(p, length)
 | 
			
		||||
	p = p[4:]
 | 
			
		||||
	p[0] = byte(paddingLength)
 | 
			
		||||
 | 
			
		||||
	// Payload.
 | 
			
		||||
	p = p[1:]
 | 
			
		||||
	copy(p, packet)
 | 
			
		||||
 | 
			
		||||
	// Padding.
 | 
			
		||||
	p = p[len(packet):]
 | 
			
		||||
	if _, err := io.ReadFull(rand, p); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.mac != nil {
 | 
			
		||||
		c.mac.Reset()
 | 
			
		||||
		binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum)
 | 
			
		||||
		c.mac.Write(c.seqNumBytes[:])
 | 
			
		||||
		c.mac.Write(c.packetData)
 | 
			
		||||
		// The MAC is now appended into the capacity reserved for it earlier.
 | 
			
		||||
		c.packetData = c.mac.Sum(c.packetData)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.encrypter.CryptBlocks(c.packetData[:encLength], c.packetData[:encLength])
 | 
			
		||||
 | 
			
		||||
	if _, err := w.Write(c.packetData); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										278
									
								
								vendor/golang.org/x/crypto/ssh/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								vendor/golang.org/x/crypto/ssh/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,278 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"os"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client implements a traditional SSH client that supports shells,
 | 
			
		||||
// subprocesses, TCP port/streamlocal forwarding and tunneled dialing.
 | 
			
		||||
type Client struct {
 | 
			
		||||
	Conn
 | 
			
		||||
 | 
			
		||||
	forwards        forwardList // forwarded tcpip connections from the remote side
 | 
			
		||||
	mu              sync.Mutex
 | 
			
		||||
	channelHandlers map[string]chan NewChannel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleChannelOpen returns a channel on which NewChannel requests
 | 
			
		||||
// for the given type are sent. If the type already is being handled,
 | 
			
		||||
// nil is returned. The channel is closed when the connection is closed.
 | 
			
		||||
func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
	if c.channelHandlers == nil {
 | 
			
		||||
		// The SSH channel has been closed.
 | 
			
		||||
		c := make(chan NewChannel)
 | 
			
		||||
		close(c)
 | 
			
		||||
		return c
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ch := c.channelHandlers[channelType]
 | 
			
		||||
	if ch != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ch = make(chan NewChannel, chanSize)
 | 
			
		||||
	c.channelHandlers[channelType] = ch
 | 
			
		||||
	return ch
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClient creates a Client on top of the given connection.
 | 
			
		||||
func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client {
 | 
			
		||||
	conn := &Client{
 | 
			
		||||
		Conn:            c,
 | 
			
		||||
		channelHandlers: make(map[string]chan NewChannel, 1),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go conn.handleGlobalRequests(reqs)
 | 
			
		||||
	go conn.handleChannelOpens(chans)
 | 
			
		||||
	go func() {
 | 
			
		||||
		conn.Wait()
 | 
			
		||||
		conn.forwards.closeAll()
 | 
			
		||||
	}()
 | 
			
		||||
	go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip"))
 | 
			
		||||
	go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-streamlocal@openssh.com"))
 | 
			
		||||
	return conn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClientConn establishes an authenticated SSH connection using c
 | 
			
		||||
// as the underlying transport.  The Request and NewChannel channels
 | 
			
		||||
// must be serviced or the connection will hang.
 | 
			
		||||
func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) {
 | 
			
		||||
	fullConf := *config
 | 
			
		||||
	fullConf.SetDefaults()
 | 
			
		||||
	if fullConf.HostKeyCallback == nil {
 | 
			
		||||
		c.Close()
 | 
			
		||||
		return nil, nil, nil, errors.New("ssh: must specify HostKeyCallback")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn := &connection{
 | 
			
		||||
		sshConn: sshConn{conn: c},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := conn.clientHandshake(addr, &fullConf); err != nil {
 | 
			
		||||
		c.Close()
 | 
			
		||||
		return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	conn.mux = newMux(conn.transport)
 | 
			
		||||
	return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clientHandshake performs the client side key exchange. See RFC 4253 Section
 | 
			
		||||
// 7.
 | 
			
		||||
func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) error {
 | 
			
		||||
	if config.ClientVersion != "" {
 | 
			
		||||
		c.clientVersion = []byte(config.ClientVersion)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.clientVersion = []byte(packageVersion)
 | 
			
		||||
	}
 | 
			
		||||
	var err error
 | 
			
		||||
	c.serverVersion, err = exchangeVersions(c.sshConn.conn, c.clientVersion)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.transport = newClientTransport(
 | 
			
		||||
		newTransport(c.sshConn.conn, config.Rand, true /* is client */),
 | 
			
		||||
		c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr())
 | 
			
		||||
	if err := c.transport.waitSession(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.sessionID = c.transport.getSessionID()
 | 
			
		||||
	return c.clientAuthenticate(config)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// verifyHostKeySignature verifies the host key obtained in the key
 | 
			
		||||
// exchange.
 | 
			
		||||
func verifyHostKeySignature(hostKey PublicKey, result *kexResult) error {
 | 
			
		||||
	sig, rest, ok := parseSignatureBody(result.Signature)
 | 
			
		||||
	if len(rest) > 0 || !ok {
 | 
			
		||||
		return errors.New("ssh: signature parse error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return hostKey.Verify(result.H, sig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSession opens a new Session for this client. (A session is a remote
 | 
			
		||||
// execution of a program.)
 | 
			
		||||
func (c *Client) NewSession() (*Session, error) {
 | 
			
		||||
	ch, in, err := c.OpenChannel("session", nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return newSession(ch, in)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) handleGlobalRequests(incoming <-chan *Request) {
 | 
			
		||||
	for r := range incoming {
 | 
			
		||||
		// This handles keepalive messages and matches
 | 
			
		||||
		// the behaviour of OpenSSH.
 | 
			
		||||
		r.Reply(false, nil)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleChannelOpens channel open messages from the remote side.
 | 
			
		||||
func (c *Client) handleChannelOpens(in <-chan NewChannel) {
 | 
			
		||||
	for ch := range in {
 | 
			
		||||
		c.mu.Lock()
 | 
			
		||||
		handler := c.channelHandlers[ch.ChannelType()]
 | 
			
		||||
		c.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
		if handler != nil {
 | 
			
		||||
			handler <- ch
 | 
			
		||||
		} else {
 | 
			
		||||
			ch.Reject(UnknownChannelType, fmt.Sprintf("unknown channel type: %v", ch.ChannelType()))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	for _, ch := range c.channelHandlers {
 | 
			
		||||
		close(ch)
 | 
			
		||||
	}
 | 
			
		||||
	c.channelHandlers = nil
 | 
			
		||||
	c.mu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dial starts a client connection to the given SSH server. It is a
 | 
			
		||||
// convenience function that connects to the given network address,
 | 
			
		||||
// initiates the SSH handshake, and then sets up a Client.  For access
 | 
			
		||||
// to incoming channels and requests, use net.Dial with NewClientConn
 | 
			
		||||
// instead.
 | 
			
		||||
func Dial(network, addr string, config *ClientConfig) (*Client, error) {
 | 
			
		||||
	conn, err := net.DialTimeout(network, addr, config.Timeout)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	c, chans, reqs, err := NewClientConn(conn, addr, config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return NewClient(c, chans, reqs), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HostKeyCallback is the function type used for verifying server
 | 
			
		||||
// keys.  A HostKeyCallback must return nil if the host key is OK, or
 | 
			
		||||
// an error to reject it. It receives the hostname as passed to Dial
 | 
			
		||||
// or NewClientConn. The remote address is the RemoteAddr of the
 | 
			
		||||
// net.Conn underlying the the SSH connection.
 | 
			
		||||
type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
 | 
			
		||||
 | 
			
		||||
// BannerCallback is the function type used for treat the banner sent by
 | 
			
		||||
// the server. A BannerCallback receives the message sent by the remote server.
 | 
			
		||||
type BannerCallback func(message string) error
 | 
			
		||||
 | 
			
		||||
// A ClientConfig structure is used to configure a Client. It must not be
 | 
			
		||||
// modified after having been passed to an SSH function.
 | 
			
		||||
type ClientConfig struct {
 | 
			
		||||
	// Config contains configuration that is shared between clients and
 | 
			
		||||
	// servers.
 | 
			
		||||
	Config
 | 
			
		||||
 | 
			
		||||
	// User contains the username to authenticate as.
 | 
			
		||||
	User string
 | 
			
		||||
 | 
			
		||||
	// Auth contains possible authentication methods to use with the
 | 
			
		||||
	// server. Only the first instance of a particular RFC 4252 method will
 | 
			
		||||
	// be used during authentication.
 | 
			
		||||
	Auth []AuthMethod
 | 
			
		||||
 | 
			
		||||
	// HostKeyCallback is called during the cryptographic
 | 
			
		||||
	// handshake to validate the server's host key. The client
 | 
			
		||||
	// configuration must supply this callback for the connection
 | 
			
		||||
	// to succeed. The functions InsecureIgnoreHostKey or
 | 
			
		||||
	// FixedHostKey can be used for simplistic host key checks.
 | 
			
		||||
	HostKeyCallback HostKeyCallback
 | 
			
		||||
 | 
			
		||||
	// BannerCallback is called during the SSH dance to display a custom
 | 
			
		||||
	// server's message. The client configuration can supply this callback to
 | 
			
		||||
	// handle it as wished. The function BannerDisplayStderr can be used for
 | 
			
		||||
	// simplistic display on Stderr.
 | 
			
		||||
	BannerCallback BannerCallback
 | 
			
		||||
 | 
			
		||||
	// ClientVersion contains the version identification string that will
 | 
			
		||||
	// be used for the connection. If empty, a reasonable default is used.
 | 
			
		||||
	ClientVersion string
 | 
			
		||||
 | 
			
		||||
	// HostKeyAlgorithms lists the key types that the client will
 | 
			
		||||
	// accept from the server as host key, in order of
 | 
			
		||||
	// preference. If empty, a reasonable default is used. Any
 | 
			
		||||
	// string returned from PublicKey.Type method may be used, or
 | 
			
		||||
	// any of the CertAlgoXxxx and KeyAlgoXxxx constants.
 | 
			
		||||
	HostKeyAlgorithms []string
 | 
			
		||||
 | 
			
		||||
	// Timeout is the maximum amount of time for the TCP connection to establish.
 | 
			
		||||
	//
 | 
			
		||||
	// A Timeout of zero means no timeout.
 | 
			
		||||
	Timeout time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InsecureIgnoreHostKey returns a function that can be used for
 | 
			
		||||
// ClientConfig.HostKeyCallback to accept any host key. It should
 | 
			
		||||
// not be used for production code.
 | 
			
		||||
func InsecureIgnoreHostKey() HostKeyCallback {
 | 
			
		||||
	return func(hostname string, remote net.Addr, key PublicKey) error {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type fixedHostKey struct {
 | 
			
		||||
	key PublicKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *fixedHostKey) check(hostname string, remote net.Addr, key PublicKey) error {
 | 
			
		||||
	if f.key == nil {
 | 
			
		||||
		return fmt.Errorf("ssh: required host key was nil")
 | 
			
		||||
	}
 | 
			
		||||
	if !bytes.Equal(key.Marshal(), f.key.Marshal()) {
 | 
			
		||||
		return fmt.Errorf("ssh: host key mismatch")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FixedHostKey returns a function for use in
 | 
			
		||||
// ClientConfig.HostKeyCallback to accept only a specific host key.
 | 
			
		||||
func FixedHostKey(key PublicKey) HostKeyCallback {
 | 
			
		||||
	hk := &fixedHostKey{key}
 | 
			
		||||
	return hk.check
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BannerDisplayStderr returns a function that can be used for
 | 
			
		||||
// ClientConfig.BannerCallback to display banners on os.Stderr.
 | 
			
		||||
func BannerDisplayStderr() BannerCallback {
 | 
			
		||||
	return func(banner string) error {
 | 
			
		||||
		_, err := os.Stderr.WriteString(banner)
 | 
			
		||||
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										510
									
								
								vendor/golang.org/x/crypto/ssh/client_auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										510
									
								
								vendor/golang.org/x/crypto/ssh/client_auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,510 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// clientAuthenticate authenticates with the remote server. See RFC 4252.
 | 
			
		||||
func (c *connection) clientAuthenticate(config *ClientConfig) error {
 | 
			
		||||
	// initiate user auth session
 | 
			
		||||
	if err := c.transport.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth})); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	packet, err := c.transport.readPacket()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	var serviceAccept serviceAcceptMsg
 | 
			
		||||
	if err := Unmarshal(packet, &serviceAccept); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// during the authentication phase the client first attempts the "none" method
 | 
			
		||||
	// then any untried methods suggested by the server.
 | 
			
		||||
	tried := make(map[string]bool)
 | 
			
		||||
	var lastMethods []string
 | 
			
		||||
 | 
			
		||||
	sessionID := c.transport.getSessionID()
 | 
			
		||||
	for auth := AuthMethod(new(noneAuth)); auth != nil; {
 | 
			
		||||
		ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if ok {
 | 
			
		||||
			// success
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		tried[auth.method()] = true
 | 
			
		||||
		if methods == nil {
 | 
			
		||||
			methods = lastMethods
 | 
			
		||||
		}
 | 
			
		||||
		lastMethods = methods
 | 
			
		||||
 | 
			
		||||
		auth = nil
 | 
			
		||||
 | 
			
		||||
	findNext:
 | 
			
		||||
		for _, a := range config.Auth {
 | 
			
		||||
			candidateMethod := a.method()
 | 
			
		||||
			if tried[candidateMethod] {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			for _, meth := range methods {
 | 
			
		||||
				if meth == candidateMethod {
 | 
			
		||||
					auth = a
 | 
			
		||||
					break findNext
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", keys(tried))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func keys(m map[string]bool) []string {
 | 
			
		||||
	s := make([]string, 0, len(m))
 | 
			
		||||
 | 
			
		||||
	for key := range m {
 | 
			
		||||
		s = append(s, key)
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An AuthMethod represents an instance of an RFC 4252 authentication method.
 | 
			
		||||
type AuthMethod interface {
 | 
			
		||||
	// auth authenticates user over transport t.
 | 
			
		||||
	// Returns true if authentication is successful.
 | 
			
		||||
	// If authentication is not successful, a []string of alternative
 | 
			
		||||
	// method names is returned. If the slice is nil, it will be ignored
 | 
			
		||||
	// and the previous set of possible methods will be reused.
 | 
			
		||||
	auth(session []byte, user string, p packetConn, rand io.Reader) (bool, []string, error)
 | 
			
		||||
 | 
			
		||||
	// method returns the RFC 4252 method name.
 | 
			
		||||
	method() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// "none" authentication, RFC 4252 section 5.2.
 | 
			
		||||
type noneAuth int
 | 
			
		||||
 | 
			
		||||
func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
 | 
			
		||||
	if err := c.writePacket(Marshal(&userAuthRequestMsg{
 | 
			
		||||
		User:    user,
 | 
			
		||||
		Service: serviceSSH,
 | 
			
		||||
		Method:  "none",
 | 
			
		||||
	})); err != nil {
 | 
			
		||||
		return false, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return handleAuthResponse(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (n *noneAuth) method() string {
 | 
			
		||||
	return "none"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// passwordCallback is an AuthMethod that fetches the password through
 | 
			
		||||
// a function call, e.g. by prompting the user.
 | 
			
		||||
type passwordCallback func() (password string, err error)
 | 
			
		||||
 | 
			
		||||
func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
 | 
			
		||||
	type passwordAuthMsg struct {
 | 
			
		||||
		User     string `sshtype:"50"`
 | 
			
		||||
		Service  string
 | 
			
		||||
		Method   string
 | 
			
		||||
		Reply    bool
 | 
			
		||||
		Password string
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pw, err := cb()
 | 
			
		||||
	// REVIEW NOTE: is there a need to support skipping a password attempt?
 | 
			
		||||
	// The program may only find out that the user doesn't have a password
 | 
			
		||||
	// when prompting.
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := c.writePacket(Marshal(&passwordAuthMsg{
 | 
			
		||||
		User:     user,
 | 
			
		||||
		Service:  serviceSSH,
 | 
			
		||||
		Method:   cb.method(),
 | 
			
		||||
		Reply:    false,
 | 
			
		||||
		Password: pw,
 | 
			
		||||
	})); err != nil {
 | 
			
		||||
		return false, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return handleAuthResponse(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cb passwordCallback) method() string {
 | 
			
		||||
	return "password"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Password returns an AuthMethod using the given password.
 | 
			
		||||
func Password(secret string) AuthMethod {
 | 
			
		||||
	return passwordCallback(func() (string, error) { return secret, nil })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PasswordCallback returns an AuthMethod that uses a callback for
 | 
			
		||||
// fetching a password.
 | 
			
		||||
func PasswordCallback(prompt func() (secret string, err error)) AuthMethod {
 | 
			
		||||
	return passwordCallback(prompt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type publickeyAuthMsg struct {
 | 
			
		||||
	User    string `sshtype:"50"`
 | 
			
		||||
	Service string
 | 
			
		||||
	Method  string
 | 
			
		||||
	// HasSig indicates to the receiver packet that the auth request is signed and
 | 
			
		||||
	// should be used for authentication of the request.
 | 
			
		||||
	HasSig   bool
 | 
			
		||||
	Algoname string
 | 
			
		||||
	PubKey   []byte
 | 
			
		||||
	// Sig is tagged with "rest" so Marshal will exclude it during
 | 
			
		||||
	// validateKey
 | 
			
		||||
	Sig []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// publicKeyCallback is an AuthMethod that uses a set of key
 | 
			
		||||
// pairs for authentication.
 | 
			
		||||
type publicKeyCallback func() ([]Signer, error)
 | 
			
		||||
 | 
			
		||||
func (cb publicKeyCallback) method() string {
 | 
			
		||||
	return "publickey"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
 | 
			
		||||
	// Authentication is performed by sending an enquiry to test if a key is
 | 
			
		||||
	// acceptable to the remote. If the key is acceptable, the client will
 | 
			
		||||
	// attempt to authenticate with the valid key.  If not the client will repeat
 | 
			
		||||
	// the process with the remaining keys.
 | 
			
		||||
 | 
			
		||||
	signers, err := cb()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	var methods []string
 | 
			
		||||
	for _, signer := range signers {
 | 
			
		||||
		ok, err := validateKey(signer.PublicKey(), user, c)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if !ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pub := signer.PublicKey()
 | 
			
		||||
		pubKey := pub.Marshal()
 | 
			
		||||
		sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{
 | 
			
		||||
			User:    user,
 | 
			
		||||
			Service: serviceSSH,
 | 
			
		||||
			Method:  cb.method(),
 | 
			
		||||
		}, []byte(pub.Type()), pubKey))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// manually wrap the serialized signature in a string
 | 
			
		||||
		s := Marshal(sign)
 | 
			
		||||
		sig := make([]byte, stringLength(len(s)))
 | 
			
		||||
		marshalString(sig, s)
 | 
			
		||||
		msg := publickeyAuthMsg{
 | 
			
		||||
			User:     user,
 | 
			
		||||
			Service:  serviceSSH,
 | 
			
		||||
			Method:   cb.method(),
 | 
			
		||||
			HasSig:   true,
 | 
			
		||||
			Algoname: pub.Type(),
 | 
			
		||||
			PubKey:   pubKey,
 | 
			
		||||
			Sig:      sig,
 | 
			
		||||
		}
 | 
			
		||||
		p := Marshal(&msg)
 | 
			
		||||
		if err := c.writePacket(p); err != nil {
 | 
			
		||||
			return false, nil, err
 | 
			
		||||
		}
 | 
			
		||||
		var success bool
 | 
			
		||||
		success, methods, err = handleAuthResponse(c)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If authentication succeeds or the list of available methods does not
 | 
			
		||||
		// contain the "publickey" method, do not attempt to authenticate with any
 | 
			
		||||
		// other keys.  According to RFC 4252 Section 7, the latter can occur when
 | 
			
		||||
		// additional authentication methods are required.
 | 
			
		||||
		if success || !containsMethod(methods, cb.method()) {
 | 
			
		||||
			return success, methods, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false, methods, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func containsMethod(methods []string, method string) bool {
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		if m == method {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// validateKey validates the key provided is acceptable to the server.
 | 
			
		||||
func validateKey(key PublicKey, user string, c packetConn) (bool, error) {
 | 
			
		||||
	pubKey := key.Marshal()
 | 
			
		||||
	msg := publickeyAuthMsg{
 | 
			
		||||
		User:     user,
 | 
			
		||||
		Service:  serviceSSH,
 | 
			
		||||
		Method:   "publickey",
 | 
			
		||||
		HasSig:   false,
 | 
			
		||||
		Algoname: key.Type(),
 | 
			
		||||
		PubKey:   pubKey,
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.writePacket(Marshal(&msg)); err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return confirmKeyAck(key, c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
 | 
			
		||||
	pubKey := key.Marshal()
 | 
			
		||||
	algoname := key.Type()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		packet, err := c.readPacket()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
		switch packet[0] {
 | 
			
		||||
		case msgUserAuthBanner:
 | 
			
		||||
			if err := handleBannerResponse(c, packet); err != nil {
 | 
			
		||||
				return false, err
 | 
			
		||||
			}
 | 
			
		||||
		case msgUserAuthPubKeyOk:
 | 
			
		||||
			var msg userAuthPubKeyOkMsg
 | 
			
		||||
			if err := Unmarshal(packet, &msg); err != nil {
 | 
			
		||||
				return false, err
 | 
			
		||||
			}
 | 
			
		||||
			if msg.Algo != algoname || !bytes.Equal(msg.PubKey, pubKey) {
 | 
			
		||||
				return false, nil
 | 
			
		||||
			}
 | 
			
		||||
			return true, nil
 | 
			
		||||
		case msgUserAuthFailure:
 | 
			
		||||
			return false, nil
 | 
			
		||||
		default:
 | 
			
		||||
			return false, unexpectedMessageError(msgUserAuthSuccess, packet[0])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PublicKeys returns an AuthMethod that uses the given key
 | 
			
		||||
// pairs.
 | 
			
		||||
func PublicKeys(signers ...Signer) AuthMethod {
 | 
			
		||||
	return publicKeyCallback(func() ([]Signer, error) { return signers, nil })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PublicKeysCallback returns an AuthMethod that runs the given
 | 
			
		||||
// function to obtain a list of key pairs.
 | 
			
		||||
func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMethod {
 | 
			
		||||
	return publicKeyCallback(getSigners)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleAuthResponse returns whether the preceding authentication request succeeded
 | 
			
		||||
// along with a list of remaining authentication methods to try next and
 | 
			
		||||
// an error if an unexpected response was received.
 | 
			
		||||
func handleAuthResponse(c packetConn) (bool, []string, error) {
 | 
			
		||||
	for {
 | 
			
		||||
		packet, err := c.readPacket()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch packet[0] {
 | 
			
		||||
		case msgUserAuthBanner:
 | 
			
		||||
			if err := handleBannerResponse(c, packet); err != nil {
 | 
			
		||||
				return false, nil, err
 | 
			
		||||
			}
 | 
			
		||||
		case msgUserAuthFailure:
 | 
			
		||||
			var msg userAuthFailureMsg
 | 
			
		||||
			if err := Unmarshal(packet, &msg); err != nil {
 | 
			
		||||
				return false, nil, err
 | 
			
		||||
			}
 | 
			
		||||
			return false, msg.Methods, nil
 | 
			
		||||
		case msgUserAuthSuccess:
 | 
			
		||||
			return true, nil, nil
 | 
			
		||||
		default:
 | 
			
		||||
			return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleBannerResponse(c packetConn, packet []byte) error {
 | 
			
		||||
	var msg userAuthBannerMsg
 | 
			
		||||
	if err := Unmarshal(packet, &msg); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	transport, ok := c.(*handshakeTransport)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if transport.bannerCallback != nil {
 | 
			
		||||
		return transport.bannerCallback(msg.Message)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KeyboardInteractiveChallenge should print questions, optionally
 | 
			
		||||
// disabling echoing (e.g. for passwords), and return all the answers.
 | 
			
		||||
// Challenge may be called multiple times in a single session. After
 | 
			
		||||
// successful authentication, the server may send a challenge with no
 | 
			
		||||
// questions, for which the user and instruction messages should be
 | 
			
		||||
// printed.  RFC 4256 section 3.3 details how the UI should behave for
 | 
			
		||||
// both CLI and GUI environments.
 | 
			
		||||
type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error)
 | 
			
		||||
 | 
			
		||||
// KeyboardInteractive returns an AuthMethod using a prompt/response
 | 
			
		||||
// sequence controlled by the server.
 | 
			
		||||
func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod {
 | 
			
		||||
	return challenge
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cb KeyboardInteractiveChallenge) method() string {
 | 
			
		||||
	return "keyboard-interactive"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
 | 
			
		||||
	type initiateMsg struct {
 | 
			
		||||
		User       string `sshtype:"50"`
 | 
			
		||||
		Service    string
 | 
			
		||||
		Method     string
 | 
			
		||||
		Language   string
 | 
			
		||||
		Submethods string
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := c.writePacket(Marshal(&initiateMsg{
 | 
			
		||||
		User:    user,
 | 
			
		||||
		Service: serviceSSH,
 | 
			
		||||
		Method:  "keyboard-interactive",
 | 
			
		||||
	})); err != nil {
 | 
			
		||||
		return false, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		packet, err := c.readPacket()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// like handleAuthResponse, but with less options.
 | 
			
		||||
		switch packet[0] {
 | 
			
		||||
		case msgUserAuthBanner:
 | 
			
		||||
			if err := handleBannerResponse(c, packet); err != nil {
 | 
			
		||||
				return false, nil, err
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		case msgUserAuthInfoRequest:
 | 
			
		||||
			// OK
 | 
			
		||||
		case msgUserAuthFailure:
 | 
			
		||||
			var msg userAuthFailureMsg
 | 
			
		||||
			if err := Unmarshal(packet, &msg); err != nil {
 | 
			
		||||
				return false, nil, err
 | 
			
		||||
			}
 | 
			
		||||
			return false, msg.Methods, nil
 | 
			
		||||
		case msgUserAuthSuccess:
 | 
			
		||||
			return true, nil, nil
 | 
			
		||||
		default:
 | 
			
		||||
			return false, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var msg userAuthInfoRequestMsg
 | 
			
		||||
		if err := Unmarshal(packet, &msg); err != nil {
 | 
			
		||||
			return false, nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Manually unpack the prompt/echo pairs.
 | 
			
		||||
		rest := msg.Prompts
 | 
			
		||||
		var prompts []string
 | 
			
		||||
		var echos []bool
 | 
			
		||||
		for i := 0; i < int(msg.NumPrompts); i++ {
 | 
			
		||||
			prompt, r, ok := parseString(rest)
 | 
			
		||||
			if !ok || len(r) == 0 {
 | 
			
		||||
				return false, nil, errors.New("ssh: prompt format error")
 | 
			
		||||
			}
 | 
			
		||||
			prompts = append(prompts, string(prompt))
 | 
			
		||||
			echos = append(echos, r[0] != 0)
 | 
			
		||||
			rest = r[1:]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(rest) != 0 {
 | 
			
		||||
			return false, nil, errors.New("ssh: extra data following keyboard-interactive pairs")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		answers, err := cb(msg.User, msg.Instruction, prompts, echos)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(answers) != len(prompts) {
 | 
			
		||||
			return false, nil, errors.New("ssh: not enough answers from keyboard-interactive callback")
 | 
			
		||||
		}
 | 
			
		||||
		responseLength := 1 + 4
 | 
			
		||||
		for _, a := range answers {
 | 
			
		||||
			responseLength += stringLength(len(a))
 | 
			
		||||
		}
 | 
			
		||||
		serialized := make([]byte, responseLength)
 | 
			
		||||
		p := serialized
 | 
			
		||||
		p[0] = msgUserAuthInfoResponse
 | 
			
		||||
		p = p[1:]
 | 
			
		||||
		p = marshalUint32(p, uint32(len(answers)))
 | 
			
		||||
		for _, a := range answers {
 | 
			
		||||
			p = marshalString(p, []byte(a))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := c.writePacket(serialized); err != nil {
 | 
			
		||||
			return false, nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type retryableAuthMethod struct {
 | 
			
		||||
	authMethod AuthMethod
 | 
			
		||||
	maxTries   int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok bool, methods []string, err error) {
 | 
			
		||||
	for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ {
 | 
			
		||||
		ok, methods, err = r.authMethod.auth(session, user, c, rand)
 | 
			
		||||
		if ok || err != nil { // either success or error terminate
 | 
			
		||||
			return ok, methods, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ok, methods, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *retryableAuthMethod) method() string {
 | 
			
		||||
	return r.authMethod.method()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RetryableAuthMethod is a decorator for other auth methods enabling them to
 | 
			
		||||
// be retried up to maxTries before considering that AuthMethod itself failed.
 | 
			
		||||
// If maxTries is <= 0, will retry indefinitely
 | 
			
		||||
//
 | 
			
		||||
// This is useful for interactive clients using challenge/response type
 | 
			
		||||
// authentication (e.g. Keyboard-Interactive, Password, etc) where the user
 | 
			
		||||
// could mistype their response resulting in the server issuing a
 | 
			
		||||
// SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4
 | 
			
		||||
// [keyboard-interactive]); Without this decorator, the non-retryable
 | 
			
		||||
// AuthMethod would be removed from future consideration, and never tried again
 | 
			
		||||
// (and so the user would never be able to retry their entry).
 | 
			
		||||
func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod {
 | 
			
		||||
	return &retryableAuthMethod{authMethod: auth, maxTries: maxTries}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										373
									
								
								vendor/golang.org/x/crypto/ssh/common.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										373
									
								
								vendor/golang.org/x/crypto/ssh/common.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,373 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	_ "crypto/sha1"
 | 
			
		||||
	_ "crypto/sha256"
 | 
			
		||||
	_ "crypto/sha512"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// These are string constants in the SSH protocol.
 | 
			
		||||
const (
 | 
			
		||||
	compressionNone = "none"
 | 
			
		||||
	serviceUserAuth = "ssh-userauth"
 | 
			
		||||
	serviceSSH      = "ssh-connection"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// supportedCiphers specifies the supported ciphers in preference order.
 | 
			
		||||
var supportedCiphers = []string{
 | 
			
		||||
	"aes128-ctr", "aes192-ctr", "aes256-ctr",
 | 
			
		||||
	"aes128-gcm@openssh.com",
 | 
			
		||||
	"arcfour256", "arcfour128",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// supportedKexAlgos specifies the supported key-exchange algorithms in
 | 
			
		||||
// preference order.
 | 
			
		||||
var supportedKexAlgos = []string{
 | 
			
		||||
	kexAlgoCurve25519SHA256,
 | 
			
		||||
	// P384 and P521 are not constant-time yet, but since we don't
 | 
			
		||||
	// reuse ephemeral keys, using them for ECDH should be OK.
 | 
			
		||||
	kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
 | 
			
		||||
	kexAlgoDH14SHA1, kexAlgoDH1SHA1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
 | 
			
		||||
// of authenticating servers) in preference order.
 | 
			
		||||
var supportedHostKeyAlgos = []string{
 | 
			
		||||
	CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
 | 
			
		||||
	CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
 | 
			
		||||
 | 
			
		||||
	KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
 | 
			
		||||
	KeyAlgoRSA, KeyAlgoDSA,
 | 
			
		||||
 | 
			
		||||
	KeyAlgoED25519,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// supportedMACs specifies a default set of MAC algorithms in preference order.
 | 
			
		||||
// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
 | 
			
		||||
// because they have reached the end of their useful life.
 | 
			
		||||
var supportedMACs = []string{
 | 
			
		||||
	"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var supportedCompressions = []string{compressionNone}
 | 
			
		||||
 | 
			
		||||
// hashFuncs keeps the mapping of supported algorithms to their respective
 | 
			
		||||
// hashes needed for signature verification.
 | 
			
		||||
var hashFuncs = map[string]crypto.Hash{
 | 
			
		||||
	KeyAlgoRSA:          crypto.SHA1,
 | 
			
		||||
	KeyAlgoDSA:          crypto.SHA1,
 | 
			
		||||
	KeyAlgoECDSA256:     crypto.SHA256,
 | 
			
		||||
	KeyAlgoECDSA384:     crypto.SHA384,
 | 
			
		||||
	KeyAlgoECDSA521:     crypto.SHA512,
 | 
			
		||||
	CertAlgoRSAv01:      crypto.SHA1,
 | 
			
		||||
	CertAlgoDSAv01:      crypto.SHA1,
 | 
			
		||||
	CertAlgoECDSA256v01: crypto.SHA256,
 | 
			
		||||
	CertAlgoECDSA384v01: crypto.SHA384,
 | 
			
		||||
	CertAlgoECDSA521v01: crypto.SHA512,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unexpectedMessageError results when the SSH message that we received didn't
 | 
			
		||||
// match what we wanted.
 | 
			
		||||
func unexpectedMessageError(expected, got uint8) error {
 | 
			
		||||
	return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseError results from a malformed SSH message.
 | 
			
		||||
func parseError(tag uint8) error {
 | 
			
		||||
	return fmt.Errorf("ssh: parse error in message type %d", tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func findCommon(what string, client []string, server []string) (common string, err error) {
 | 
			
		||||
	for _, c := range client {
 | 
			
		||||
		for _, s := range server {
 | 
			
		||||
			if c == s {
 | 
			
		||||
				return c, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type directionAlgorithms struct {
 | 
			
		||||
	Cipher      string
 | 
			
		||||
	MAC         string
 | 
			
		||||
	Compression string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rekeyBytes returns a rekeying intervals in bytes.
 | 
			
		||||
func (a *directionAlgorithms) rekeyBytes() int64 {
 | 
			
		||||
	// According to RFC4344 block ciphers should rekey after
 | 
			
		||||
	// 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
 | 
			
		||||
	// 128.
 | 
			
		||||
	switch a.Cipher {
 | 
			
		||||
	case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcmCipherID, aes128cbcID:
 | 
			
		||||
		return 16 * (1 << 32)
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// For others, stick with RFC4253 recommendation to rekey after 1 Gb of data.
 | 
			
		||||
	return 1 << 30
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type algorithms struct {
 | 
			
		||||
	kex     string
 | 
			
		||||
	hostKey string
 | 
			
		||||
	w       directionAlgorithms
 | 
			
		||||
	r       directionAlgorithms
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
 | 
			
		||||
	result := &algorithms{}
 | 
			
		||||
 | 
			
		||||
	result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.w.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.r.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.w.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.r.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.w.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.r.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// If rekeythreshold is too small, we can't make any progress sending
 | 
			
		||||
// stuff.
 | 
			
		||||
const minRekeyThreshold uint64 = 256
 | 
			
		||||
 | 
			
		||||
// Config contains configuration data common to both ServerConfig and
 | 
			
		||||
// ClientConfig.
 | 
			
		||||
type Config struct {
 | 
			
		||||
	// Rand provides the source of entropy for cryptographic
 | 
			
		||||
	// primitives. If Rand is nil, the cryptographic random reader
 | 
			
		||||
	// in package crypto/rand will be used.
 | 
			
		||||
	Rand io.Reader
 | 
			
		||||
 | 
			
		||||
	// The maximum number of bytes sent or received after which a
 | 
			
		||||
	// new key is negotiated. It must be at least 256. If
 | 
			
		||||
	// unspecified, a size suitable for the chosen cipher is used.
 | 
			
		||||
	RekeyThreshold uint64
 | 
			
		||||
 | 
			
		||||
	// The allowed key exchanges algorithms. If unspecified then a
 | 
			
		||||
	// default set of algorithms is used.
 | 
			
		||||
	KeyExchanges []string
 | 
			
		||||
 | 
			
		||||
	// The allowed cipher algorithms. If unspecified then a sensible
 | 
			
		||||
	// default is used.
 | 
			
		||||
	Ciphers []string
 | 
			
		||||
 | 
			
		||||
	// The allowed MAC algorithms. If unspecified then a sensible default
 | 
			
		||||
	// is used.
 | 
			
		||||
	MACs []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetDefaults sets sensible values for unset fields in config. This is
 | 
			
		||||
// exported for testing: Configs passed to SSH functions are copied and have
 | 
			
		||||
// default values set automatically.
 | 
			
		||||
func (c *Config) SetDefaults() {
 | 
			
		||||
	if c.Rand == nil {
 | 
			
		||||
		c.Rand = rand.Reader
 | 
			
		||||
	}
 | 
			
		||||
	if c.Ciphers == nil {
 | 
			
		||||
		c.Ciphers = supportedCiphers
 | 
			
		||||
	}
 | 
			
		||||
	var ciphers []string
 | 
			
		||||
	for _, c := range c.Ciphers {
 | 
			
		||||
		if cipherModes[c] != nil {
 | 
			
		||||
			// reject the cipher if we have no cipherModes definition
 | 
			
		||||
			ciphers = append(ciphers, c)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	c.Ciphers = ciphers
 | 
			
		||||
 | 
			
		||||
	if c.KeyExchanges == nil {
 | 
			
		||||
		c.KeyExchanges = supportedKexAlgos
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.MACs == nil {
 | 
			
		||||
		c.MACs = supportedMACs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.RekeyThreshold == 0 {
 | 
			
		||||
		// cipher specific default
 | 
			
		||||
	} else if c.RekeyThreshold < minRekeyThreshold {
 | 
			
		||||
		c.RekeyThreshold = minRekeyThreshold
 | 
			
		||||
	} else if c.RekeyThreshold >= math.MaxInt64 {
 | 
			
		||||
		// Avoid weirdness if somebody uses -1 as a threshold.
 | 
			
		||||
		c.RekeyThreshold = math.MaxInt64
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// buildDataSignedForAuth returns the data that is signed in order to prove
 | 
			
		||||
// possession of a private key. See RFC 4252, section 7.
 | 
			
		||||
func buildDataSignedForAuth(sessionID []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte {
 | 
			
		||||
	data := struct {
 | 
			
		||||
		Session []byte
 | 
			
		||||
		Type    byte
 | 
			
		||||
		User    string
 | 
			
		||||
		Service string
 | 
			
		||||
		Method  string
 | 
			
		||||
		Sign    bool
 | 
			
		||||
		Algo    []byte
 | 
			
		||||
		PubKey  []byte
 | 
			
		||||
	}{
 | 
			
		||||
		sessionID,
 | 
			
		||||
		msgUserAuthRequest,
 | 
			
		||||
		req.User,
 | 
			
		||||
		req.Service,
 | 
			
		||||
		req.Method,
 | 
			
		||||
		true,
 | 
			
		||||
		algo,
 | 
			
		||||
		pubKey,
 | 
			
		||||
	}
 | 
			
		||||
	return Marshal(data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func appendU16(buf []byte, n uint16) []byte {
 | 
			
		||||
	return append(buf, byte(n>>8), byte(n))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func appendU32(buf []byte, n uint32) []byte {
 | 
			
		||||
	return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func appendU64(buf []byte, n uint64) []byte {
 | 
			
		||||
	return append(buf,
 | 
			
		||||
		byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32),
 | 
			
		||||
		byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func appendInt(buf []byte, n int) []byte {
 | 
			
		||||
	return appendU32(buf, uint32(n))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func appendString(buf []byte, s string) []byte {
 | 
			
		||||
	buf = appendU32(buf, uint32(len(s)))
 | 
			
		||||
	buf = append(buf, s...)
 | 
			
		||||
	return buf
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func appendBool(buf []byte, b bool) []byte {
 | 
			
		||||
	if b {
 | 
			
		||||
		return append(buf, 1)
 | 
			
		||||
	}
 | 
			
		||||
	return append(buf, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newCond is a helper to hide the fact that there is no usable zero
 | 
			
		||||
// value for sync.Cond.
 | 
			
		||||
func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) }
 | 
			
		||||
 | 
			
		||||
// window represents the buffer available to clients
 | 
			
		||||
// wishing to write to a channel.
 | 
			
		||||
type window struct {
 | 
			
		||||
	*sync.Cond
 | 
			
		||||
	win          uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1
 | 
			
		||||
	writeWaiters int
 | 
			
		||||
	closed       bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// add adds win to the amount of window available
 | 
			
		||||
// for consumers.
 | 
			
		||||
func (w *window) add(win uint32) bool {
 | 
			
		||||
	// a zero sized window adjust is a noop.
 | 
			
		||||
	if win == 0 {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	w.L.Lock()
 | 
			
		||||
	if w.win+win < win {
 | 
			
		||||
		w.L.Unlock()
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	w.win += win
 | 
			
		||||
	// It is unusual that multiple goroutines would be attempting to reserve
 | 
			
		||||
	// window space, but not guaranteed. Use broadcast to notify all waiters
 | 
			
		||||
	// that additional window is available.
 | 
			
		||||
	w.Broadcast()
 | 
			
		||||
	w.L.Unlock()
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// close sets the window to closed, so all reservations fail
 | 
			
		||||
// immediately.
 | 
			
		||||
func (w *window) close() {
 | 
			
		||||
	w.L.Lock()
 | 
			
		||||
	w.closed = true
 | 
			
		||||
	w.Broadcast()
 | 
			
		||||
	w.L.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// reserve reserves win from the available window capacity.
 | 
			
		||||
// If no capacity remains, reserve will block. reserve may
 | 
			
		||||
// return less than requested.
 | 
			
		||||
func (w *window) reserve(win uint32) (uint32, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
	w.L.Lock()
 | 
			
		||||
	w.writeWaiters++
 | 
			
		||||
	w.Broadcast()
 | 
			
		||||
	for w.win == 0 && !w.closed {
 | 
			
		||||
		w.Wait()
 | 
			
		||||
	}
 | 
			
		||||
	w.writeWaiters--
 | 
			
		||||
	if w.win < win {
 | 
			
		||||
		win = w.win
 | 
			
		||||
	}
 | 
			
		||||
	w.win -= win
 | 
			
		||||
	if w.closed {
 | 
			
		||||
		err = io.EOF
 | 
			
		||||
	}
 | 
			
		||||
	w.L.Unlock()
 | 
			
		||||
	return win, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// waitWriterBlocked waits until some goroutine is blocked for further
 | 
			
		||||
// writes. It is used in tests only.
 | 
			
		||||
func (w *window) waitWriterBlocked() {
 | 
			
		||||
	w.Cond.L.Lock()
 | 
			
		||||
	for w.writeWaiters == 0 {
 | 
			
		||||
		w.Cond.Wait()
 | 
			
		||||
	}
 | 
			
		||||
	w.Cond.L.Unlock()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										143
									
								
								vendor/golang.org/x/crypto/ssh/connection.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/golang.org/x/crypto/ssh/connection.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
			
		||||
// Copyright 2013 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// OpenChannelError is returned if the other side rejects an
 | 
			
		||||
// OpenChannel request.
 | 
			
		||||
type OpenChannelError struct {
 | 
			
		||||
	Reason  RejectionReason
 | 
			
		||||
	Message string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *OpenChannelError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("ssh: rejected: %s (%s)", e.Reason, e.Message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConnMetadata holds metadata for the connection.
 | 
			
		||||
type ConnMetadata interface {
 | 
			
		||||
	// User returns the user ID for this connection.
 | 
			
		||||
	User() string
 | 
			
		||||
 | 
			
		||||
	// SessionID returns the session hash, also denoted by H.
 | 
			
		||||
	SessionID() []byte
 | 
			
		||||
 | 
			
		||||
	// ClientVersion returns the client's version string as hashed
 | 
			
		||||
	// into the session ID.
 | 
			
		||||
	ClientVersion() []byte
 | 
			
		||||
 | 
			
		||||
	// ServerVersion returns the server's version string as hashed
 | 
			
		||||
	// into the session ID.
 | 
			
		||||
	ServerVersion() []byte
 | 
			
		||||
 | 
			
		||||
	// RemoteAddr returns the remote address for this connection.
 | 
			
		||||
	RemoteAddr() net.Addr
 | 
			
		||||
 | 
			
		||||
	// LocalAddr returns the local address for this connection.
 | 
			
		||||
	LocalAddr() net.Addr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Conn represents an SSH connection for both server and client roles.
 | 
			
		||||
// Conn is the basis for implementing an application layer, such
 | 
			
		||||
// as ClientConn, which implements the traditional shell access for
 | 
			
		||||
// clients.
 | 
			
		||||
type Conn interface {
 | 
			
		||||
	ConnMetadata
 | 
			
		||||
 | 
			
		||||
	// SendRequest sends a global request, and returns the
 | 
			
		||||
	// reply. If wantReply is true, it returns the response status
 | 
			
		||||
	// and payload. See also RFC4254, section 4.
 | 
			
		||||
	SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error)
 | 
			
		||||
 | 
			
		||||
	// OpenChannel tries to open an channel. If the request is
 | 
			
		||||
	// rejected, it returns *OpenChannelError. On success it returns
 | 
			
		||||
	// the SSH Channel and a Go channel for incoming, out-of-band
 | 
			
		||||
	// requests. The Go channel must be serviced, or the
 | 
			
		||||
	// connection will hang.
 | 
			
		||||
	OpenChannel(name string, data []byte) (Channel, <-chan *Request, error)
 | 
			
		||||
 | 
			
		||||
	// Close closes the underlying network connection
 | 
			
		||||
	Close() error
 | 
			
		||||
 | 
			
		||||
	// Wait blocks until the connection has shut down, and returns the
 | 
			
		||||
	// error causing the shutdown.
 | 
			
		||||
	Wait() error
 | 
			
		||||
 | 
			
		||||
	// TODO(hanwen): consider exposing:
 | 
			
		||||
	//   RequestKeyChange
 | 
			
		||||
	//   Disconnect
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DiscardRequests consumes and rejects all requests from the
 | 
			
		||||
// passed-in channel.
 | 
			
		||||
func DiscardRequests(in <-chan *Request) {
 | 
			
		||||
	for req := range in {
 | 
			
		||||
		if req.WantReply {
 | 
			
		||||
			req.Reply(false, nil)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A connection represents an incoming connection.
 | 
			
		||||
type connection struct {
 | 
			
		||||
	transport *handshakeTransport
 | 
			
		||||
	sshConn
 | 
			
		||||
 | 
			
		||||
	// The connection protocol.
 | 
			
		||||
	*mux
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *connection) Close() error {
 | 
			
		||||
	return c.sshConn.conn.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sshconn provides net.Conn metadata, but disallows direct reads and
 | 
			
		||||
// writes.
 | 
			
		||||
type sshConn struct {
 | 
			
		||||
	conn net.Conn
 | 
			
		||||
 | 
			
		||||
	user          string
 | 
			
		||||
	sessionID     []byte
 | 
			
		||||
	clientVersion []byte
 | 
			
		||||
	serverVersion []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func dup(src []byte) []byte {
 | 
			
		||||
	dst := make([]byte, len(src))
 | 
			
		||||
	copy(dst, src)
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *sshConn) User() string {
 | 
			
		||||
	return c.user
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *sshConn) RemoteAddr() net.Addr {
 | 
			
		||||
	return c.conn.RemoteAddr()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *sshConn) Close() error {
 | 
			
		||||
	return c.conn.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *sshConn) LocalAddr() net.Addr {
 | 
			
		||||
	return c.conn.LocalAddr()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *sshConn) SessionID() []byte {
 | 
			
		||||
	return dup(c.sessionID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *sshConn) ClientVersion() []byte {
 | 
			
		||||
	return dup(c.clientVersion)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *sshConn) ServerVersion() []byte {
 | 
			
		||||
	return dup(c.serverVersion)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/golang.org/x/crypto/ssh/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/golang.org/x/crypto/ssh/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Package ssh implements an SSH client and server.
 | 
			
		||||
 | 
			
		||||
SSH is a transport security protocol, an authentication protocol and a
 | 
			
		||||
family of application protocols. The most typical application level
 | 
			
		||||
protocol is a remote shell and this is specifically implemented.  However,
 | 
			
		||||
the multiplexed nature of SSH is exposed to users that wish to support
 | 
			
		||||
others.
 | 
			
		||||
 | 
			
		||||
References:
 | 
			
		||||
  [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD
 | 
			
		||||
  [SSH-PARAMETERS]:    http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
 | 
			
		||||
 | 
			
		||||
This package does not fall under the stability promise of the Go language itself,
 | 
			
		||||
so its API may be changed when pressing needs arise.
 | 
			
		||||
*/
 | 
			
		||||
package ssh // import "golang.org/x/crypto/ssh"
 | 
			
		||||
							
								
								
									
										646
									
								
								vendor/golang.org/x/crypto/ssh/handshake.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										646
									
								
								vendor/golang.org/x/crypto/ssh/handshake.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,646 @@
 | 
			
		||||
// Copyright 2013 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// debugHandshake, if set, prints messages sent and received.  Key
 | 
			
		||||
// exchange messages are printed as if DH were used, so the debug
 | 
			
		||||
// messages are wrong when using ECDH.
 | 
			
		||||
const debugHandshake = false
 | 
			
		||||
 | 
			
		||||
// chanSize sets the amount of buffering SSH connections. This is
 | 
			
		||||
// primarily for testing: setting chanSize=0 uncovers deadlocks more
 | 
			
		||||
// quickly.
 | 
			
		||||
const chanSize = 16
 | 
			
		||||
 | 
			
		||||
// keyingTransport is a packet based transport that supports key
 | 
			
		||||
// changes. It need not be thread-safe. It should pass through
 | 
			
		||||
// msgNewKeys in both directions.
 | 
			
		||||
type keyingTransport interface {
 | 
			
		||||
	packetConn
 | 
			
		||||
 | 
			
		||||
	// prepareKeyChange sets up a key change. The key change for a
 | 
			
		||||
	// direction will be effected if a msgNewKeys message is sent
 | 
			
		||||
	// or received.
 | 
			
		||||
	prepareKeyChange(*algorithms, *kexResult) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handshakeTransport implements rekeying on top of a keyingTransport
 | 
			
		||||
// and offers a thread-safe writePacket() interface.
 | 
			
		||||
type handshakeTransport struct {
 | 
			
		||||
	conn   keyingTransport
 | 
			
		||||
	config *Config
 | 
			
		||||
 | 
			
		||||
	serverVersion []byte
 | 
			
		||||
	clientVersion []byte
 | 
			
		||||
 | 
			
		||||
	// hostKeys is non-empty if we are the server. In that case,
 | 
			
		||||
	// it contains all host keys that can be used to sign the
 | 
			
		||||
	// connection.
 | 
			
		||||
	hostKeys []Signer
 | 
			
		||||
 | 
			
		||||
	// hostKeyAlgorithms is non-empty if we are the client. In that case,
 | 
			
		||||
	// we accept these key types from the server as host key.
 | 
			
		||||
	hostKeyAlgorithms []string
 | 
			
		||||
 | 
			
		||||
	// On read error, incoming is closed, and readError is set.
 | 
			
		||||
	incoming  chan []byte
 | 
			
		||||
	readError error
 | 
			
		||||
 | 
			
		||||
	mu             sync.Mutex
 | 
			
		||||
	writeError     error
 | 
			
		||||
	sentInitPacket []byte
 | 
			
		||||
	sentInitMsg    *kexInitMsg
 | 
			
		||||
	pendingPackets [][]byte // Used when a key exchange is in progress.
 | 
			
		||||
 | 
			
		||||
	// If the read loop wants to schedule a kex, it pings this
 | 
			
		||||
	// channel, and the write loop will send out a kex
 | 
			
		||||
	// message.
 | 
			
		||||
	requestKex chan struct{}
 | 
			
		||||
 | 
			
		||||
	// If the other side requests or confirms a kex, its kexInit
 | 
			
		||||
	// packet is sent here for the write loop to find it.
 | 
			
		||||
	startKex chan *pendingKex
 | 
			
		||||
 | 
			
		||||
	// data for host key checking
 | 
			
		||||
	hostKeyCallback HostKeyCallback
 | 
			
		||||
	dialAddress     string
 | 
			
		||||
	remoteAddr      net.Addr
 | 
			
		||||
 | 
			
		||||
	// bannerCallback is non-empty if we are the client and it has been set in
 | 
			
		||||
	// ClientConfig. In that case it is called during the user authentication
 | 
			
		||||
	// dance to handle a custom server's message.
 | 
			
		||||
	bannerCallback BannerCallback
 | 
			
		||||
 | 
			
		||||
	// Algorithms agreed in the last key exchange.
 | 
			
		||||
	algorithms *algorithms
 | 
			
		||||
 | 
			
		||||
	readPacketsLeft uint32
 | 
			
		||||
	readBytesLeft   int64
 | 
			
		||||
 | 
			
		||||
	writePacketsLeft uint32
 | 
			
		||||
	writeBytesLeft   int64
 | 
			
		||||
 | 
			
		||||
	// The session ID or nil if first kex did not complete yet.
 | 
			
		||||
	sessionID []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type pendingKex struct {
 | 
			
		||||
	otherInit []byte
 | 
			
		||||
	done      chan error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport {
 | 
			
		||||
	t := &handshakeTransport{
 | 
			
		||||
		conn:          conn,
 | 
			
		||||
		serverVersion: serverVersion,
 | 
			
		||||
		clientVersion: clientVersion,
 | 
			
		||||
		incoming:      make(chan []byte, chanSize),
 | 
			
		||||
		requestKex:    make(chan struct{}, 1),
 | 
			
		||||
		startKex:      make(chan *pendingKex, 1),
 | 
			
		||||
 | 
			
		||||
		config: config,
 | 
			
		||||
	}
 | 
			
		||||
	t.resetReadThresholds()
 | 
			
		||||
	t.resetWriteThresholds()
 | 
			
		||||
 | 
			
		||||
	// We always start with a mandatory key exchange.
 | 
			
		||||
	t.requestKex <- struct{}{}
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ClientConfig, dialAddr string, addr net.Addr) *handshakeTransport {
 | 
			
		||||
	t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion)
 | 
			
		||||
	t.dialAddress = dialAddr
 | 
			
		||||
	t.remoteAddr = addr
 | 
			
		||||
	t.hostKeyCallback = config.HostKeyCallback
 | 
			
		||||
	t.bannerCallback = config.BannerCallback
 | 
			
		||||
	if config.HostKeyAlgorithms != nil {
 | 
			
		||||
		t.hostKeyAlgorithms = config.HostKeyAlgorithms
 | 
			
		||||
	} else {
 | 
			
		||||
		t.hostKeyAlgorithms = supportedHostKeyAlgos
 | 
			
		||||
	}
 | 
			
		||||
	go t.readLoop()
 | 
			
		||||
	go t.kexLoop()
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ServerConfig) *handshakeTransport {
 | 
			
		||||
	t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion)
 | 
			
		||||
	t.hostKeys = config.hostKeys
 | 
			
		||||
	go t.readLoop()
 | 
			
		||||
	go t.kexLoop()
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) getSessionID() []byte {
 | 
			
		||||
	return t.sessionID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// waitSession waits for the session to be established. This should be
 | 
			
		||||
// the first thing to call after instantiating handshakeTransport.
 | 
			
		||||
func (t *handshakeTransport) waitSession() error {
 | 
			
		||||
	p, err := t.readPacket()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if p[0] != msgNewKeys {
 | 
			
		||||
		return fmt.Errorf("ssh: first packet should be msgNewKeys")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) id() string {
 | 
			
		||||
	if len(t.hostKeys) > 0 {
 | 
			
		||||
		return "server"
 | 
			
		||||
	}
 | 
			
		||||
	return "client"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) printPacket(p []byte, write bool) {
 | 
			
		||||
	action := "got"
 | 
			
		||||
	if write {
 | 
			
		||||
		action = "sent"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p[0] == msgChannelData || p[0] == msgChannelExtendedData {
 | 
			
		||||
		log.Printf("%s %s data (packet %d bytes)", t.id(), action, len(p))
 | 
			
		||||
	} else {
 | 
			
		||||
		msg, err := decode(p)
 | 
			
		||||
		log.Printf("%s %s %T %v (%v)", t.id(), action, msg, msg, err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) readPacket() ([]byte, error) {
 | 
			
		||||
	p, ok := <-t.incoming
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, t.readError
 | 
			
		||||
	}
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) readLoop() {
 | 
			
		||||
	first := true
 | 
			
		||||
	for {
 | 
			
		||||
		p, err := t.readOnePacket(first)
 | 
			
		||||
		first = false
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.readError = err
 | 
			
		||||
			close(t.incoming)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if p[0] == msgIgnore || p[0] == msgDebug {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		t.incoming <- p
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Stop writers too.
 | 
			
		||||
	t.recordWriteError(t.readError)
 | 
			
		||||
 | 
			
		||||
	// Unblock the writer should it wait for this.
 | 
			
		||||
	close(t.startKex)
 | 
			
		||||
 | 
			
		||||
	// Don't close t.requestKex; it's also written to from writePacket.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) pushPacket(p []byte) error {
 | 
			
		||||
	if debugHandshake {
 | 
			
		||||
		t.printPacket(p, true)
 | 
			
		||||
	}
 | 
			
		||||
	return t.conn.writePacket(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) getWriteError() error {
 | 
			
		||||
	t.mu.Lock()
 | 
			
		||||
	defer t.mu.Unlock()
 | 
			
		||||
	return t.writeError
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) recordWriteError(err error) {
 | 
			
		||||
	t.mu.Lock()
 | 
			
		||||
	defer t.mu.Unlock()
 | 
			
		||||
	if t.writeError == nil && err != nil {
 | 
			
		||||
		t.writeError = err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) requestKeyExchange() {
 | 
			
		||||
	select {
 | 
			
		||||
	case t.requestKex <- struct{}{}:
 | 
			
		||||
	default:
 | 
			
		||||
		// something already requested a kex, so do nothing.
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) resetWriteThresholds() {
 | 
			
		||||
	t.writePacketsLeft = packetRekeyThreshold
 | 
			
		||||
	if t.config.RekeyThreshold > 0 {
 | 
			
		||||
		t.writeBytesLeft = int64(t.config.RekeyThreshold)
 | 
			
		||||
	} else if t.algorithms != nil {
 | 
			
		||||
		t.writeBytesLeft = t.algorithms.w.rekeyBytes()
 | 
			
		||||
	} else {
 | 
			
		||||
		t.writeBytesLeft = 1 << 30
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) kexLoop() {
 | 
			
		||||
 | 
			
		||||
write:
 | 
			
		||||
	for t.getWriteError() == nil {
 | 
			
		||||
		var request *pendingKex
 | 
			
		||||
		var sent bool
 | 
			
		||||
 | 
			
		||||
		for request == nil || !sent {
 | 
			
		||||
			var ok bool
 | 
			
		||||
			select {
 | 
			
		||||
			case request, ok = <-t.startKex:
 | 
			
		||||
				if !ok {
 | 
			
		||||
					break write
 | 
			
		||||
				}
 | 
			
		||||
			case <-t.requestKex:
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if !sent {
 | 
			
		||||
				if err := t.sendKexInit(); err != nil {
 | 
			
		||||
					t.recordWriteError(err)
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				sent = true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := t.getWriteError(); err != nil {
 | 
			
		||||
			if request != nil {
 | 
			
		||||
				request.done <- err
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// We're not servicing t.requestKex, but that is OK:
 | 
			
		||||
		// we never block on sending to t.requestKex.
 | 
			
		||||
 | 
			
		||||
		// We're not servicing t.startKex, but the remote end
 | 
			
		||||
		// has just sent us a kexInitMsg, so it can't send
 | 
			
		||||
		// another key change request, until we close the done
 | 
			
		||||
		// channel on the pendingKex request.
 | 
			
		||||
 | 
			
		||||
		err := t.enterKeyExchange(request.otherInit)
 | 
			
		||||
 | 
			
		||||
		t.mu.Lock()
 | 
			
		||||
		t.writeError = err
 | 
			
		||||
		t.sentInitPacket = nil
 | 
			
		||||
		t.sentInitMsg = nil
 | 
			
		||||
 | 
			
		||||
		t.resetWriteThresholds()
 | 
			
		||||
 | 
			
		||||
		// we have completed the key exchange. Since the
 | 
			
		||||
		// reader is still blocked, it is safe to clear out
 | 
			
		||||
		// the requestKex channel. This avoids the situation
 | 
			
		||||
		// where: 1) we consumed our own request for the
 | 
			
		||||
		// initial kex, and 2) the kex from the remote side
 | 
			
		||||
		// caused another send on the requestKex channel,
 | 
			
		||||
	clear:
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case <-t.requestKex:
 | 
			
		||||
				//
 | 
			
		||||
			default:
 | 
			
		||||
				break clear
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		request.done <- t.writeError
 | 
			
		||||
 | 
			
		||||
		// kex finished. Push packets that we received while
 | 
			
		||||
		// the kex was in progress. Don't look at t.startKex
 | 
			
		||||
		// and don't increment writtenSinceKex: if we trigger
 | 
			
		||||
		// another kex while we are still busy with the last
 | 
			
		||||
		// one, things will become very confusing.
 | 
			
		||||
		for _, p := range t.pendingPackets {
 | 
			
		||||
			t.writeError = t.pushPacket(p)
 | 
			
		||||
			if t.writeError != nil {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		t.pendingPackets = t.pendingPackets[:0]
 | 
			
		||||
		t.mu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// drain startKex channel. We don't service t.requestKex
 | 
			
		||||
	// because nobody does blocking sends there.
 | 
			
		||||
	go func() {
 | 
			
		||||
		for init := range t.startKex {
 | 
			
		||||
			init.done <- t.writeError
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// Unblock reader.
 | 
			
		||||
	t.conn.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The protocol uses uint32 for packet counters, so we can't let them
 | 
			
		||||
// reach 1<<32.  We will actually read and write more packets than
 | 
			
		||||
// this, though: the other side may send more packets, and after we
 | 
			
		||||
// hit this limit on writing we will send a few more packets for the
 | 
			
		||||
// key exchange itself.
 | 
			
		||||
const packetRekeyThreshold = (1 << 31)
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) resetReadThresholds() {
 | 
			
		||||
	t.readPacketsLeft = packetRekeyThreshold
 | 
			
		||||
	if t.config.RekeyThreshold > 0 {
 | 
			
		||||
		t.readBytesLeft = int64(t.config.RekeyThreshold)
 | 
			
		||||
	} else if t.algorithms != nil {
 | 
			
		||||
		t.readBytesLeft = t.algorithms.r.rekeyBytes()
 | 
			
		||||
	} else {
 | 
			
		||||
		t.readBytesLeft = 1 << 30
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) {
 | 
			
		||||
	p, err := t.conn.readPacket()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t.readPacketsLeft > 0 {
 | 
			
		||||
		t.readPacketsLeft--
 | 
			
		||||
	} else {
 | 
			
		||||
		t.requestKeyExchange()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t.readBytesLeft > 0 {
 | 
			
		||||
		t.readBytesLeft -= int64(len(p))
 | 
			
		||||
	} else {
 | 
			
		||||
		t.requestKeyExchange()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if debugHandshake {
 | 
			
		||||
		t.printPacket(p, false)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if first && p[0] != msgKexInit {
 | 
			
		||||
		return nil, fmt.Errorf("ssh: first packet should be msgKexInit")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p[0] != msgKexInit {
 | 
			
		||||
		return p, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	firstKex := t.sessionID == nil
 | 
			
		||||
 | 
			
		||||
	kex := pendingKex{
 | 
			
		||||
		done:      make(chan error, 1),
 | 
			
		||||
		otherInit: p,
 | 
			
		||||
	}
 | 
			
		||||
	t.startKex <- &kex
 | 
			
		||||
	err = <-kex.done
 | 
			
		||||
 | 
			
		||||
	if debugHandshake {
 | 
			
		||||
		log.Printf("%s exited key exchange (first %v), err %v", t.id(), firstKex, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.resetReadThresholds()
 | 
			
		||||
 | 
			
		||||
	// By default, a key exchange is hidden from higher layers by
 | 
			
		||||
	// translating it into msgIgnore.
 | 
			
		||||
	successPacket := []byte{msgIgnore}
 | 
			
		||||
	if firstKex {
 | 
			
		||||
		// sendKexInit() for the first kex waits for
 | 
			
		||||
		// msgNewKeys so the authentication process is
 | 
			
		||||
		// guaranteed to happen over an encrypted transport.
 | 
			
		||||
		successPacket = []byte{msgNewKeys}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return successPacket, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sendKexInit sends a key change message.
 | 
			
		||||
func (t *handshakeTransport) sendKexInit() error {
 | 
			
		||||
	t.mu.Lock()
 | 
			
		||||
	defer t.mu.Unlock()
 | 
			
		||||
	if t.sentInitMsg != nil {
 | 
			
		||||
		// kexInits may be sent either in response to the other side,
 | 
			
		||||
		// or because our side wants to initiate a key change, so we
 | 
			
		||||
		// may have already sent a kexInit. In that case, don't send a
 | 
			
		||||
		// second kexInit.
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg := &kexInitMsg{
 | 
			
		||||
		KexAlgos:                t.config.KeyExchanges,
 | 
			
		||||
		CiphersClientServer:     t.config.Ciphers,
 | 
			
		||||
		CiphersServerClient:     t.config.Ciphers,
 | 
			
		||||
		MACsClientServer:        t.config.MACs,
 | 
			
		||||
		MACsServerClient:        t.config.MACs,
 | 
			
		||||
		CompressionClientServer: supportedCompressions,
 | 
			
		||||
		CompressionServerClient: supportedCompressions,
 | 
			
		||||
	}
 | 
			
		||||
	io.ReadFull(rand.Reader, msg.Cookie[:])
 | 
			
		||||
 | 
			
		||||
	if len(t.hostKeys) > 0 {
 | 
			
		||||
		for _, k := range t.hostKeys {
 | 
			
		||||
			msg.ServerHostKeyAlgos = append(
 | 
			
		||||
				msg.ServerHostKeyAlgos, k.PublicKey().Type())
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		msg.ServerHostKeyAlgos = t.hostKeyAlgorithms
 | 
			
		||||
	}
 | 
			
		||||
	packet := Marshal(msg)
 | 
			
		||||
 | 
			
		||||
	// writePacket destroys the contents, so save a copy.
 | 
			
		||||
	packetCopy := make([]byte, len(packet))
 | 
			
		||||
	copy(packetCopy, packet)
 | 
			
		||||
 | 
			
		||||
	if err := t.pushPacket(packetCopy); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.sentInitMsg = msg
 | 
			
		||||
	t.sentInitPacket = packet
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) writePacket(p []byte) error {
 | 
			
		||||
	switch p[0] {
 | 
			
		||||
	case msgKexInit:
 | 
			
		||||
		return errors.New("ssh: only handshakeTransport can send kexInit")
 | 
			
		||||
	case msgNewKeys:
 | 
			
		||||
		return errors.New("ssh: only handshakeTransport can send newKeys")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.mu.Lock()
 | 
			
		||||
	defer t.mu.Unlock()
 | 
			
		||||
	if t.writeError != nil {
 | 
			
		||||
		return t.writeError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t.sentInitMsg != nil {
 | 
			
		||||
		// Copy the packet so the writer can reuse the buffer.
 | 
			
		||||
		cp := make([]byte, len(p))
 | 
			
		||||
		copy(cp, p)
 | 
			
		||||
		t.pendingPackets = append(t.pendingPackets, cp)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t.writeBytesLeft > 0 {
 | 
			
		||||
		t.writeBytesLeft -= int64(len(p))
 | 
			
		||||
	} else {
 | 
			
		||||
		t.requestKeyExchange()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t.writePacketsLeft > 0 {
 | 
			
		||||
		t.writePacketsLeft--
 | 
			
		||||
	} else {
 | 
			
		||||
		t.requestKeyExchange()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := t.pushPacket(p); err != nil {
 | 
			
		||||
		t.writeError = err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) Close() error {
 | 
			
		||||
	return t.conn.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
 | 
			
		||||
	if debugHandshake {
 | 
			
		||||
		log.Printf("%s entered key exchange", t.id())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	otherInit := &kexInitMsg{}
 | 
			
		||||
	if err := Unmarshal(otherInitPacket, otherInit); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	magics := handshakeMagics{
 | 
			
		||||
		clientVersion: t.clientVersion,
 | 
			
		||||
		serverVersion: t.serverVersion,
 | 
			
		||||
		clientKexInit: otherInitPacket,
 | 
			
		||||
		serverKexInit: t.sentInitPacket,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clientInit := otherInit
 | 
			
		||||
	serverInit := t.sentInitMsg
 | 
			
		||||
	if len(t.hostKeys) == 0 {
 | 
			
		||||
		clientInit, serverInit = serverInit, clientInit
 | 
			
		||||
 | 
			
		||||
		magics.clientKexInit = t.sentInitPacket
 | 
			
		||||
		magics.serverKexInit = otherInitPacket
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	t.algorithms, err = findAgreedAlgorithms(clientInit, serverInit)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We don't send FirstKexFollows, but we handle receiving it.
 | 
			
		||||
	//
 | 
			
		||||
	// RFC 4253 section 7 defines the kex and the agreement method for
 | 
			
		||||
	// first_kex_packet_follows. It states that the guessed packet
 | 
			
		||||
	// should be ignored if the "kex algorithm and/or the host
 | 
			
		||||
	// key algorithm is guessed wrong (server and client have
 | 
			
		||||
	// different preferred algorithm), or if any of the other
 | 
			
		||||
	// algorithms cannot be agreed upon". The other algorithms have
 | 
			
		||||
	// already been checked above so the kex algorithm and host key
 | 
			
		||||
	// algorithm are checked here.
 | 
			
		||||
	if otherInit.FirstKexFollows && (clientInit.KexAlgos[0] != serverInit.KexAlgos[0] || clientInit.ServerHostKeyAlgos[0] != serverInit.ServerHostKeyAlgos[0]) {
 | 
			
		||||
		// other side sent a kex message for the wrong algorithm,
 | 
			
		||||
		// which we have to ignore.
 | 
			
		||||
		if _, err := t.conn.readPacket(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kex, ok := kexAlgoMap[t.algorithms.kex]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.kex)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var result *kexResult
 | 
			
		||||
	if len(t.hostKeys) > 0 {
 | 
			
		||||
		result, err = t.server(kex, t.algorithms, &magics)
 | 
			
		||||
	} else {
 | 
			
		||||
		result, err = t.client(kex, t.algorithms, &magics)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t.sessionID == nil {
 | 
			
		||||
		t.sessionID = result.H
 | 
			
		||||
	}
 | 
			
		||||
	result.SessionID = t.sessionID
 | 
			
		||||
 | 
			
		||||
	if err := t.conn.prepareKeyChange(t.algorithms, result); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if packet, err := t.conn.readPacket(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	} else if packet[0] != msgNewKeys {
 | 
			
		||||
		return unexpectedMessageError(msgNewKeys, packet[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) server(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) {
 | 
			
		||||
	var hostKey Signer
 | 
			
		||||
	for _, k := range t.hostKeys {
 | 
			
		||||
		if algs.hostKey == k.PublicKey().Type() {
 | 
			
		||||
			hostKey = k
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey)
 | 
			
		||||
	return r, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) {
 | 
			
		||||
	result, err := kex.Client(t.conn, t.config.Rand, magics)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hostKey, err := ParsePublicKey(result.HostKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := verifyHostKeySignature(hostKey, result); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										540
									
								
								vendor/golang.org/x/crypto/ssh/kex.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										540
									
								
								vendor/golang.org/x/crypto/ssh/kex.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,540 @@
 | 
			
		||||
// Copyright 2013 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto"
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/subtle"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math/big"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/curve25519"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	kexAlgoDH1SHA1          = "diffie-hellman-group1-sha1"
 | 
			
		||||
	kexAlgoDH14SHA1         = "diffie-hellman-group14-sha1"
 | 
			
		||||
	kexAlgoECDH256          = "ecdh-sha2-nistp256"
 | 
			
		||||
	kexAlgoECDH384          = "ecdh-sha2-nistp384"
 | 
			
		||||
	kexAlgoECDH521          = "ecdh-sha2-nistp521"
 | 
			
		||||
	kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// kexResult captures the outcome of a key exchange.
 | 
			
		||||
type kexResult struct {
 | 
			
		||||
	// Session hash. See also RFC 4253, section 8.
 | 
			
		||||
	H []byte
 | 
			
		||||
 | 
			
		||||
	// Shared secret. See also RFC 4253, section 8.
 | 
			
		||||
	K []byte
 | 
			
		||||
 | 
			
		||||
	// Host key as hashed into H.
 | 
			
		||||
	HostKey []byte
 | 
			
		||||
 | 
			
		||||
	// Signature of H.
 | 
			
		||||
	Signature []byte
 | 
			
		||||
 | 
			
		||||
	// A cryptographic hash function that matches the security
 | 
			
		||||
	// level of the key exchange algorithm. It is used for
 | 
			
		||||
	// calculating H, and for deriving keys from H and K.
 | 
			
		||||
	Hash crypto.Hash
 | 
			
		||||
 | 
			
		||||
	// The session ID, which is the first H computed. This is used
 | 
			
		||||
	// to derive key material inside the transport.
 | 
			
		||||
	SessionID []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handshakeMagics contains data that is always included in the
 | 
			
		||||
// session hash.
 | 
			
		||||
type handshakeMagics struct {
 | 
			
		||||
	clientVersion, serverVersion []byte
 | 
			
		||||
	clientKexInit, serverKexInit []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *handshakeMagics) write(w io.Writer) {
 | 
			
		||||
	writeString(w, m.clientVersion)
 | 
			
		||||
	writeString(w, m.serverVersion)
 | 
			
		||||
	writeString(w, m.clientKexInit)
 | 
			
		||||
	writeString(w, m.serverKexInit)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// kexAlgorithm abstracts different key exchange algorithms.
 | 
			
		||||
type kexAlgorithm interface {
 | 
			
		||||
	// Server runs server-side key agreement, signing the result
 | 
			
		||||
	// with a hostkey.
 | 
			
		||||
	Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error)
 | 
			
		||||
 | 
			
		||||
	// Client runs the client-side key agreement. Caller is
 | 
			
		||||
	// responsible for verifying the host key signature.
 | 
			
		||||
	Client(p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
 | 
			
		||||
type dhGroup struct {
 | 
			
		||||
	g, p, pMinus1 *big.Int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
 | 
			
		||||
	if theirPublic.Cmp(bigOne) <= 0 || theirPublic.Cmp(group.pMinus1) >= 0 {
 | 
			
		||||
		return nil, errors.New("ssh: DH parameter out of bounds")
 | 
			
		||||
	}
 | 
			
		||||
	return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
 | 
			
		||||
	hashFunc := crypto.SHA1
 | 
			
		||||
 | 
			
		||||
	var x *big.Int
 | 
			
		||||
	for {
 | 
			
		||||
		var err error
 | 
			
		||||
		if x, err = rand.Int(randSource, group.pMinus1); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if x.Sign() > 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	X := new(big.Int).Exp(group.g, x, group.p)
 | 
			
		||||
	kexDHInit := kexDHInitMsg{
 | 
			
		||||
		X: X,
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.writePacket(Marshal(&kexDHInit)); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packet, err := c.readPacket()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var kexDHReply kexDHReplyMsg
 | 
			
		||||
	if err = Unmarshal(packet, &kexDHReply); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ki, err := group.diffieHellman(kexDHReply.Y, x)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	h := hashFunc.New()
 | 
			
		||||
	magics.write(h)
 | 
			
		||||
	writeString(h, kexDHReply.HostKey)
 | 
			
		||||
	writeInt(h, X)
 | 
			
		||||
	writeInt(h, kexDHReply.Y)
 | 
			
		||||
	K := make([]byte, intLength(ki))
 | 
			
		||||
	marshalInt(K, ki)
 | 
			
		||||
	h.Write(K)
 | 
			
		||||
 | 
			
		||||
	return &kexResult{
 | 
			
		||||
		H:         h.Sum(nil),
 | 
			
		||||
		K:         K,
 | 
			
		||||
		HostKey:   kexDHReply.HostKey,
 | 
			
		||||
		Signature: kexDHReply.Signature,
 | 
			
		||||
		Hash:      crypto.SHA1,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
 | 
			
		||||
	hashFunc := crypto.SHA1
 | 
			
		||||
	packet, err := c.readPacket()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var kexDHInit kexDHInitMsg
 | 
			
		||||
	if err = Unmarshal(packet, &kexDHInit); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var y *big.Int
 | 
			
		||||
	for {
 | 
			
		||||
		if y, err = rand.Int(randSource, group.pMinus1); err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if y.Sign() > 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Y := new(big.Int).Exp(group.g, y, group.p)
 | 
			
		||||
	ki, err := group.diffieHellman(kexDHInit.X, y)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hostKeyBytes := priv.PublicKey().Marshal()
 | 
			
		||||
 | 
			
		||||
	h := hashFunc.New()
 | 
			
		||||
	magics.write(h)
 | 
			
		||||
	writeString(h, hostKeyBytes)
 | 
			
		||||
	writeInt(h, kexDHInit.X)
 | 
			
		||||
	writeInt(h, Y)
 | 
			
		||||
 | 
			
		||||
	K := make([]byte, intLength(ki))
 | 
			
		||||
	marshalInt(K, ki)
 | 
			
		||||
	h.Write(K)
 | 
			
		||||
 | 
			
		||||
	H := h.Sum(nil)
 | 
			
		||||
 | 
			
		||||
	// H is already a hash, but the hostkey signing will apply its
 | 
			
		||||
	// own key-specific hash algorithm.
 | 
			
		||||
	sig, err := signAndMarshal(priv, randSource, H)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kexDHReply := kexDHReplyMsg{
 | 
			
		||||
		HostKey:   hostKeyBytes,
 | 
			
		||||
		Y:         Y,
 | 
			
		||||
		Signature: sig,
 | 
			
		||||
	}
 | 
			
		||||
	packet = Marshal(&kexDHReply)
 | 
			
		||||
 | 
			
		||||
	err = c.writePacket(packet)
 | 
			
		||||
	return &kexResult{
 | 
			
		||||
		H:         H,
 | 
			
		||||
		K:         K,
 | 
			
		||||
		HostKey:   hostKeyBytes,
 | 
			
		||||
		Signature: sig,
 | 
			
		||||
		Hash:      crypto.SHA1,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ecdh performs Elliptic Curve Diffie-Hellman key exchange as
 | 
			
		||||
// described in RFC 5656, section 4.
 | 
			
		||||
type ecdh struct {
 | 
			
		||||
	curve elliptic.Curve
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
 | 
			
		||||
	ephKey, err := ecdsa.GenerateKey(kex.curve, rand)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kexInit := kexECDHInitMsg{
 | 
			
		||||
		ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	serialized := Marshal(&kexInit)
 | 
			
		||||
	if err := c.writePacket(serialized); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packet, err := c.readPacket()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var reply kexECDHReplyMsg
 | 
			
		||||
	if err = Unmarshal(packet, &reply); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// generate shared secret
 | 
			
		||||
	secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes())
 | 
			
		||||
 | 
			
		||||
	h := ecHash(kex.curve).New()
 | 
			
		||||
	magics.write(h)
 | 
			
		||||
	writeString(h, reply.HostKey)
 | 
			
		||||
	writeString(h, kexInit.ClientPubKey)
 | 
			
		||||
	writeString(h, reply.EphemeralPubKey)
 | 
			
		||||
	K := make([]byte, intLength(secret))
 | 
			
		||||
	marshalInt(K, secret)
 | 
			
		||||
	h.Write(K)
 | 
			
		||||
 | 
			
		||||
	return &kexResult{
 | 
			
		||||
		H:         h.Sum(nil),
 | 
			
		||||
		K:         K,
 | 
			
		||||
		HostKey:   reply.HostKey,
 | 
			
		||||
		Signature: reply.Signature,
 | 
			
		||||
		Hash:      ecHash(kex.curve),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unmarshalECKey parses and checks an EC key.
 | 
			
		||||
func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) {
 | 
			
		||||
	x, y = elliptic.Unmarshal(curve, pubkey)
 | 
			
		||||
	if x == nil {
 | 
			
		||||
		return nil, nil, errors.New("ssh: elliptic.Unmarshal failure")
 | 
			
		||||
	}
 | 
			
		||||
	if !validateECPublicKey(curve, x, y) {
 | 
			
		||||
		return nil, nil, errors.New("ssh: public key not on curve")
 | 
			
		||||
	}
 | 
			
		||||
	return x, y, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// validateECPublicKey checks that the point is a valid public key for
 | 
			
		||||
// the given curve. See [SEC1], 3.2.2
 | 
			
		||||
func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool {
 | 
			
		||||
	if x.Sign() == 0 && y.Sign() == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if x.Cmp(curve.Params().P) >= 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if y.Cmp(curve.Params().P) >= 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !curve.IsOnCurve(x, y) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We don't check if N * PubKey == 0, since
 | 
			
		||||
	//
 | 
			
		||||
	// - the NIST curves have cofactor = 1, so this is implicit.
 | 
			
		||||
	// (We don't foresee an implementation that supports non NIST
 | 
			
		||||
	// curves)
 | 
			
		||||
	//
 | 
			
		||||
	// - for ephemeral keys, we don't need to worry about small
 | 
			
		||||
	// subgroup attacks.
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
 | 
			
		||||
	packet, err := c.readPacket()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var kexECDHInit kexECDHInitMsg
 | 
			
		||||
	if err = Unmarshal(packet, &kexECDHInit); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We could cache this key across multiple users/multiple
 | 
			
		||||
	// connection attempts, but the benefit is small. OpenSSH
 | 
			
		||||
	// generates a new key for each incoming connection.
 | 
			
		||||
	ephKey, err := ecdsa.GenerateKey(kex.curve, rand)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hostKeyBytes := priv.PublicKey().Marshal()
 | 
			
		||||
 | 
			
		||||
	serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y)
 | 
			
		||||
 | 
			
		||||
	// generate shared secret
 | 
			
		||||
	secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes())
 | 
			
		||||
 | 
			
		||||
	h := ecHash(kex.curve).New()
 | 
			
		||||
	magics.write(h)
 | 
			
		||||
	writeString(h, hostKeyBytes)
 | 
			
		||||
	writeString(h, kexECDHInit.ClientPubKey)
 | 
			
		||||
	writeString(h, serializedEphKey)
 | 
			
		||||
 | 
			
		||||
	K := make([]byte, intLength(secret))
 | 
			
		||||
	marshalInt(K, secret)
 | 
			
		||||
	h.Write(K)
 | 
			
		||||
 | 
			
		||||
	H := h.Sum(nil)
 | 
			
		||||
 | 
			
		||||
	// H is already a hash, but the hostkey signing will apply its
 | 
			
		||||
	// own key-specific hash algorithm.
 | 
			
		||||
	sig, err := signAndMarshal(priv, rand, H)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reply := kexECDHReplyMsg{
 | 
			
		||||
		EphemeralPubKey: serializedEphKey,
 | 
			
		||||
		HostKey:         hostKeyBytes,
 | 
			
		||||
		Signature:       sig,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	serialized := Marshal(&reply)
 | 
			
		||||
	if err := c.writePacket(serialized); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &kexResult{
 | 
			
		||||
		H:         H,
 | 
			
		||||
		K:         K,
 | 
			
		||||
		HostKey:   reply.HostKey,
 | 
			
		||||
		Signature: sig,
 | 
			
		||||
		Hash:      ecHash(kex.curve),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var kexAlgoMap = map[string]kexAlgorithm{}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	// This is the group called diffie-hellman-group1-sha1 in RFC
 | 
			
		||||
	// 4253 and Oakley Group 2 in RFC 2409.
 | 
			
		||||
	p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
 | 
			
		||||
	kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
 | 
			
		||||
		g:       new(big.Int).SetInt64(2),
 | 
			
		||||
		p:       p,
 | 
			
		||||
		pMinus1: new(big.Int).Sub(p, bigOne),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// This is the group called diffie-hellman-group14-sha1 in RFC
 | 
			
		||||
	// 4253 and Oakley Group 14 in RFC 3526.
 | 
			
		||||
	p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
 | 
			
		||||
 | 
			
		||||
	kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
 | 
			
		||||
		g:       new(big.Int).SetInt64(2),
 | 
			
		||||
		p:       p,
 | 
			
		||||
		pMinus1: new(big.Int).Sub(p, bigOne),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
 | 
			
		||||
	kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
 | 
			
		||||
	kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
 | 
			
		||||
	kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// curve25519sha256 implements the curve25519-sha256@libssh.org key
 | 
			
		||||
// agreement protocol, as described in
 | 
			
		||||
// https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt
 | 
			
		||||
type curve25519sha256 struct{}
 | 
			
		||||
 | 
			
		||||
type curve25519KeyPair struct {
 | 
			
		||||
	priv [32]byte
 | 
			
		||||
	pub  [32]byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kp *curve25519KeyPair) generate(rand io.Reader) error {
 | 
			
		||||
	if _, err := io.ReadFull(rand, kp.priv[:]); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	curve25519.ScalarBaseMult(&kp.pub, &kp.priv)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// curve25519Zeros is just an array of 32 zero bytes so that we have something
 | 
			
		||||
// convenient to compare against in order to reject curve25519 points with the
 | 
			
		||||
// wrong order.
 | 
			
		||||
var curve25519Zeros [32]byte
 | 
			
		||||
 | 
			
		||||
func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
 | 
			
		||||
	var kp curve25519KeyPair
 | 
			
		||||
	if err := kp.generate(rand); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packet, err := c.readPacket()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var reply kexECDHReplyMsg
 | 
			
		||||
	if err = Unmarshal(packet, &reply); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(reply.EphemeralPubKey) != 32 {
 | 
			
		||||
		return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var servPub, secret [32]byte
 | 
			
		||||
	copy(servPub[:], reply.EphemeralPubKey)
 | 
			
		||||
	curve25519.ScalarMult(&secret, &kp.priv, &servPub)
 | 
			
		||||
	if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
 | 
			
		||||
		return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	h := crypto.SHA256.New()
 | 
			
		||||
	magics.write(h)
 | 
			
		||||
	writeString(h, reply.HostKey)
 | 
			
		||||
	writeString(h, kp.pub[:])
 | 
			
		||||
	writeString(h, reply.EphemeralPubKey)
 | 
			
		||||
 | 
			
		||||
	ki := new(big.Int).SetBytes(secret[:])
 | 
			
		||||
	K := make([]byte, intLength(ki))
 | 
			
		||||
	marshalInt(K, ki)
 | 
			
		||||
	h.Write(K)
 | 
			
		||||
 | 
			
		||||
	return &kexResult{
 | 
			
		||||
		H:         h.Sum(nil),
 | 
			
		||||
		K:         K,
 | 
			
		||||
		HostKey:   reply.HostKey,
 | 
			
		||||
		Signature: reply.Signature,
 | 
			
		||||
		Hash:      crypto.SHA256,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
 | 
			
		||||
	packet, err := c.readPacket()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var kexInit kexECDHInitMsg
 | 
			
		||||
	if err = Unmarshal(packet, &kexInit); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(kexInit.ClientPubKey) != 32 {
 | 
			
		||||
		return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var kp curve25519KeyPair
 | 
			
		||||
	if err := kp.generate(rand); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var clientPub, secret [32]byte
 | 
			
		||||
	copy(clientPub[:], kexInit.ClientPubKey)
 | 
			
		||||
	curve25519.ScalarMult(&secret, &kp.priv, &clientPub)
 | 
			
		||||
	if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
 | 
			
		||||
		return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hostKeyBytes := priv.PublicKey().Marshal()
 | 
			
		||||
 | 
			
		||||
	h := crypto.SHA256.New()
 | 
			
		||||
	magics.write(h)
 | 
			
		||||
	writeString(h, hostKeyBytes)
 | 
			
		||||
	writeString(h, kexInit.ClientPubKey)
 | 
			
		||||
	writeString(h, kp.pub[:])
 | 
			
		||||
 | 
			
		||||
	ki := new(big.Int).SetBytes(secret[:])
 | 
			
		||||
	K := make([]byte, intLength(ki))
 | 
			
		||||
	marshalInt(K, ki)
 | 
			
		||||
	h.Write(K)
 | 
			
		||||
 | 
			
		||||
	H := h.Sum(nil)
 | 
			
		||||
 | 
			
		||||
	sig, err := signAndMarshal(priv, rand, H)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reply := kexECDHReplyMsg{
 | 
			
		||||
		EphemeralPubKey: kp.pub[:],
 | 
			
		||||
		HostKey:         hostKeyBytes,
 | 
			
		||||
		Signature:       sig,
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.writePacket(Marshal(&reply)); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &kexResult{
 | 
			
		||||
		H:         H,
 | 
			
		||||
		K:         K,
 | 
			
		||||
		HostKey:   hostKeyBytes,
 | 
			
		||||
		Signature: sig,
 | 
			
		||||
		Hash:      crypto.SHA256,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1031
									
								
								vendor/golang.org/x/crypto/ssh/keys.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1031
									
								
								vendor/golang.org/x/crypto/ssh/keys.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										546
									
								
								vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										546
									
								
								vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,546 @@
 | 
			
		||||
// Copyright 2017 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Package knownhosts implements a parser for the OpenSSH
 | 
			
		||||
// known_hosts host key database.
 | 
			
		||||
package knownhosts
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/hmac"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/ssh"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// See the sshd manpage
 | 
			
		||||
// (http://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT) for
 | 
			
		||||
// background.
 | 
			
		||||
 | 
			
		||||
type addr struct{ host, port string }
 | 
			
		||||
 | 
			
		||||
func (a *addr) String() string {
 | 
			
		||||
	h := a.host
 | 
			
		||||
	if strings.Contains(h, ":") {
 | 
			
		||||
		h = "[" + h + "]"
 | 
			
		||||
	}
 | 
			
		||||
	return h + ":" + a.port
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type matcher interface {
 | 
			
		||||
	match([]addr) bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type hostPattern struct {
 | 
			
		||||
	negate bool
 | 
			
		||||
	addr   addr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *hostPattern) String() string {
 | 
			
		||||
	n := ""
 | 
			
		||||
	if p.negate {
 | 
			
		||||
		n = "!"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return n + p.addr.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type hostPatterns []hostPattern
 | 
			
		||||
 | 
			
		||||
func (ps hostPatterns) match(addrs []addr) bool {
 | 
			
		||||
	matched := false
 | 
			
		||||
	for _, p := range ps {
 | 
			
		||||
		for _, a := range addrs {
 | 
			
		||||
			m := p.match(a)
 | 
			
		||||
			if !m {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if p.negate {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			matched = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return matched
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See
 | 
			
		||||
// https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/addrmatch.c
 | 
			
		||||
// The matching of * has no regard for separators, unlike filesystem globs
 | 
			
		||||
func wildcardMatch(pat []byte, str []byte) bool {
 | 
			
		||||
	for {
 | 
			
		||||
		if len(pat) == 0 {
 | 
			
		||||
			return len(str) == 0
 | 
			
		||||
		}
 | 
			
		||||
		if len(str) == 0 {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if pat[0] == '*' {
 | 
			
		||||
			if len(pat) == 1 {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for j := range str {
 | 
			
		||||
				if wildcardMatch(pat[1:], str[j:]) {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if pat[0] == '?' || pat[0] == str[0] {
 | 
			
		||||
			pat = pat[1:]
 | 
			
		||||
			str = str[1:]
 | 
			
		||||
		} else {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *hostPattern) match(a addr) bool {
 | 
			
		||||
	return wildcardMatch([]byte(p.addr.host), []byte(a.host)) && p.addr.port == a.port
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type keyDBLine struct {
 | 
			
		||||
	cert     bool
 | 
			
		||||
	matcher  matcher
 | 
			
		||||
	knownKey KnownKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func serialize(k ssh.PublicKey) string {
 | 
			
		||||
	return k.Type() + " " + base64.StdEncoding.EncodeToString(k.Marshal())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *keyDBLine) match(addrs []addr) bool {
 | 
			
		||||
	return l.matcher.match(addrs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type hostKeyDB struct {
 | 
			
		||||
	// Serialized version of revoked keys
 | 
			
		||||
	revoked map[string]*KnownKey
 | 
			
		||||
	lines   []keyDBLine
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newHostKeyDB() *hostKeyDB {
 | 
			
		||||
	db := &hostKeyDB{
 | 
			
		||||
		revoked: make(map[string]*KnownKey),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return db
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func keyEq(a, b ssh.PublicKey) bool {
 | 
			
		||||
	return bytes.Equal(a.Marshal(), b.Marshal())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsAuthorityForHost can be used as a callback in ssh.CertChecker
 | 
			
		||||
func (db *hostKeyDB) IsHostAuthority(remote ssh.PublicKey, address string) bool {
 | 
			
		||||
	h, p, err := net.SplitHostPort(address)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	a := addr{host: h, port: p}
 | 
			
		||||
 | 
			
		||||
	for _, l := range db.lines {
 | 
			
		||||
		if l.cert && keyEq(l.knownKey.Key, remote) && l.match([]addr{a}) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsRevoked can be used as a callback in ssh.CertChecker
 | 
			
		||||
func (db *hostKeyDB) IsRevoked(key *ssh.Certificate) bool {
 | 
			
		||||
	_, ok := db.revoked[string(key.Marshal())]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const markerCert = "@cert-authority"
 | 
			
		||||
const markerRevoked = "@revoked"
 | 
			
		||||
 | 
			
		||||
func nextWord(line []byte) (string, []byte) {
 | 
			
		||||
	i := bytes.IndexAny(line, "\t ")
 | 
			
		||||
	if i == -1 {
 | 
			
		||||
		return string(line), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return string(line[:i]), bytes.TrimSpace(line[i:])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseLine(line []byte) (marker, host string, key ssh.PublicKey, err error) {
 | 
			
		||||
	if w, next := nextWord(line); w == markerCert || w == markerRevoked {
 | 
			
		||||
		marker = w
 | 
			
		||||
		line = next
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	host, line = nextWord(line)
 | 
			
		||||
	if len(line) == 0 {
 | 
			
		||||
		return "", "", nil, errors.New("knownhosts: missing host pattern")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// ignore the keytype as it's in the key blob anyway.
 | 
			
		||||
	_, line = nextWord(line)
 | 
			
		||||
	if len(line) == 0 {
 | 
			
		||||
		return "", "", nil, errors.New("knownhosts: missing key type pattern")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	keyBlob, _ := nextWord(line)
 | 
			
		||||
 | 
			
		||||
	keyBytes, err := base64.StdEncoding.DecodeString(keyBlob)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", nil, err
 | 
			
		||||
	}
 | 
			
		||||
	key, err = ssh.ParsePublicKey(keyBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return marker, host, key, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error {
 | 
			
		||||
	marker, pattern, key, err := parseLine(line)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if marker == markerRevoked {
 | 
			
		||||
		db.revoked[string(key.Marshal())] = &KnownKey{
 | 
			
		||||
			Key:      key,
 | 
			
		||||
			Filename: filename,
 | 
			
		||||
			Line:     linenum,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	entry := keyDBLine{
 | 
			
		||||
		cert: marker == markerCert,
 | 
			
		||||
		knownKey: KnownKey{
 | 
			
		||||
			Filename: filename,
 | 
			
		||||
			Line:     linenum,
 | 
			
		||||
			Key:      key,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if pattern[0] == '|' {
 | 
			
		||||
		entry.matcher, err = newHashedHost(pattern)
 | 
			
		||||
	} else {
 | 
			
		||||
		entry.matcher, err = newHostnameMatcher(pattern)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db.lines = append(db.lines, entry)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newHostnameMatcher(pattern string) (matcher, error) {
 | 
			
		||||
	var hps hostPatterns
 | 
			
		||||
	for _, p := range strings.Split(pattern, ",") {
 | 
			
		||||
		if len(p) == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var a addr
 | 
			
		||||
		var negate bool
 | 
			
		||||
		if p[0] == '!' {
 | 
			
		||||
			negate = true
 | 
			
		||||
			p = p[1:]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(p) == 0 {
 | 
			
		||||
			return nil, errors.New("knownhosts: negation without following hostname")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var err error
 | 
			
		||||
		if p[0] == '[' {
 | 
			
		||||
			a.host, a.port, err = net.SplitHostPort(p)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			a.host, a.port, err = net.SplitHostPort(p)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				a.host = p
 | 
			
		||||
				a.port = "22"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		hps = append(hps, hostPattern{
 | 
			
		||||
			negate: negate,
 | 
			
		||||
			addr:   a,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	return hps, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KnownKey represents a key declared in a known_hosts file.
 | 
			
		||||
type KnownKey struct {
 | 
			
		||||
	Key      ssh.PublicKey
 | 
			
		||||
	Filename string
 | 
			
		||||
	Line     int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (k *KnownKey) String() string {
 | 
			
		||||
	return fmt.Sprintf("%s:%d: %s", k.Filename, k.Line, serialize(k.Key))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KeyError is returned if we did not find the key in the host key
 | 
			
		||||
// database, or there was a mismatch.  Typically, in batch
 | 
			
		||||
// applications, this should be interpreted as failure. Interactive
 | 
			
		||||
// applications can offer an interactive prompt to the user.
 | 
			
		||||
type KeyError struct {
 | 
			
		||||
	// Want holds the accepted host keys. For each key algorithm,
 | 
			
		||||
	// there can be one hostkey.  If Want is empty, the host is
 | 
			
		||||
	// unknown. If Want is non-empty, there was a mismatch, which
 | 
			
		||||
	// can signify a MITM attack.
 | 
			
		||||
	Want []KnownKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *KeyError) Error() string {
 | 
			
		||||
	if len(u.Want) == 0 {
 | 
			
		||||
		return "knownhosts: key is unknown"
 | 
			
		||||
	}
 | 
			
		||||
	return "knownhosts: key mismatch"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RevokedError is returned if we found a key that was revoked.
 | 
			
		||||
type RevokedError struct {
 | 
			
		||||
	Revoked KnownKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *RevokedError) Error() string {
 | 
			
		||||
	return "knownhosts: key is revoked"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// check checks a key against the host database. This should not be
 | 
			
		||||
// used for verifying certificates.
 | 
			
		||||
func (db *hostKeyDB) check(address string, remote net.Addr, remoteKey ssh.PublicKey) error {
 | 
			
		||||
	if revoked := db.revoked[string(remoteKey.Marshal())]; revoked != nil {
 | 
			
		||||
		return &RevokedError{Revoked: *revoked}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	host, port, err := net.SplitHostPort(remote.String())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("knownhosts: SplitHostPort(%s): %v", remote, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addrs := []addr{
 | 
			
		||||
		{host, port},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if address != "" {
 | 
			
		||||
		host, port, err := net.SplitHostPort(address)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("knownhosts: SplitHostPort(%s): %v", address, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		addrs = append(addrs, addr{host, port})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return db.checkAddrs(addrs, remoteKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// checkAddrs checks if we can find the given public key for any of
 | 
			
		||||
// the given addresses.  If we only find an entry for the IP address,
 | 
			
		||||
// or only the hostname, then this still succeeds.
 | 
			
		||||
func (db *hostKeyDB) checkAddrs(addrs []addr, remoteKey ssh.PublicKey) error {
 | 
			
		||||
	// TODO(hanwen): are these the right semantics? What if there
 | 
			
		||||
	// is just a key for the IP address, but not for the
 | 
			
		||||
	// hostname?
 | 
			
		||||
 | 
			
		||||
	// Algorithm => key.
 | 
			
		||||
	knownKeys := map[string]KnownKey{}
 | 
			
		||||
	for _, l := range db.lines {
 | 
			
		||||
		if l.match(addrs) {
 | 
			
		||||
			typ := l.knownKey.Key.Type()
 | 
			
		||||
			if _, ok := knownKeys[typ]; !ok {
 | 
			
		||||
				knownKeys[typ] = l.knownKey
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	keyErr := &KeyError{}
 | 
			
		||||
	for _, v := range knownKeys {
 | 
			
		||||
		keyErr.Want = append(keyErr.Want, v)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Unknown remote host.
 | 
			
		||||
	if len(knownKeys) == 0 {
 | 
			
		||||
		return keyErr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If the remote host starts using a different, unknown key type, we
 | 
			
		||||
	// also interpret that as a mismatch.
 | 
			
		||||
	if known, ok := knownKeys[remoteKey.Type()]; !ok || !keyEq(known.Key, remoteKey) {
 | 
			
		||||
		return keyErr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The Read function parses file contents.
 | 
			
		||||
func (db *hostKeyDB) Read(r io.Reader, filename string) error {
 | 
			
		||||
	scanner := bufio.NewScanner(r)
 | 
			
		||||
 | 
			
		||||
	lineNum := 0
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		lineNum++
 | 
			
		||||
		line := scanner.Bytes()
 | 
			
		||||
		line = bytes.TrimSpace(line)
 | 
			
		||||
		if len(line) == 0 || line[0] == '#' {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := db.parseLine(line, filename, lineNum); err != nil {
 | 
			
		||||
			return fmt.Errorf("knownhosts: %s:%d: %v", filename, lineNum, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return scanner.Err()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New creates a host key callback from the given OpenSSH host key
 | 
			
		||||
// files. The returned callback is for use in
 | 
			
		||||
// ssh.ClientConfig.HostKeyCallback. Hashed hostnames are not supported.
 | 
			
		||||
func New(files ...string) (ssh.HostKeyCallback, error) {
 | 
			
		||||
	db := newHostKeyDB()
 | 
			
		||||
	for _, fn := range files {
 | 
			
		||||
		f, err := os.Open(fn)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		defer f.Close()
 | 
			
		||||
		if err := db.Read(f, fn); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var certChecker ssh.CertChecker
 | 
			
		||||
	certChecker.IsHostAuthority = db.IsHostAuthority
 | 
			
		||||
	certChecker.IsRevoked = db.IsRevoked
 | 
			
		||||
	certChecker.HostKeyFallback = db.check
 | 
			
		||||
 | 
			
		||||
	return certChecker.CheckHostKey, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Normalize normalizes an address into the form used in known_hosts
 | 
			
		||||
func Normalize(address string) string {
 | 
			
		||||
	host, port, err := net.SplitHostPort(address)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		host = address
 | 
			
		||||
		port = "22"
 | 
			
		||||
	}
 | 
			
		||||
	entry := host
 | 
			
		||||
	if port != "22" {
 | 
			
		||||
		entry = "[" + entry + "]:" + port
 | 
			
		||||
	} else if strings.Contains(host, ":") && !strings.HasPrefix(host, "[") {
 | 
			
		||||
		entry = "[" + entry + "]"
 | 
			
		||||
	}
 | 
			
		||||
	return entry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Line returns a line to add append to the known_hosts files.
 | 
			
		||||
func Line(addresses []string, key ssh.PublicKey) string {
 | 
			
		||||
	var trimmed []string
 | 
			
		||||
	for _, a := range addresses {
 | 
			
		||||
		trimmed = append(trimmed, Normalize(a))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return strings.Join(trimmed, ",") + " " + serialize(key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HashHostname hashes the given hostname. The hostname is not
 | 
			
		||||
// normalized before hashing.
 | 
			
		||||
func HashHostname(hostname string) string {
 | 
			
		||||
	// TODO(hanwen): check if we can safely normalize this always.
 | 
			
		||||
	salt := make([]byte, sha1.Size)
 | 
			
		||||
 | 
			
		||||
	_, err := rand.Read(salt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(fmt.Sprintf("crypto/rand failure %v", err))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hash := hashHost(hostname, salt)
 | 
			
		||||
	return encodeHash(sha1HashType, salt, hash)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func decodeHash(encoded string) (hashType string, salt, hash []byte, err error) {
 | 
			
		||||
	if len(encoded) == 0 || encoded[0] != '|' {
 | 
			
		||||
		err = errors.New("knownhosts: hashed host must start with '|'")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	components := strings.Split(encoded, "|")
 | 
			
		||||
	if len(components) != 4 {
 | 
			
		||||
		err = fmt.Errorf("knownhosts: got %d components, want 3", len(components))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hashType = components[1]
 | 
			
		||||
	if salt, err = base64.StdEncoding.DecodeString(components[2]); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if hash, err = base64.StdEncoding.DecodeString(components[3]); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func encodeHash(typ string, salt []byte, hash []byte) string {
 | 
			
		||||
	return strings.Join([]string{"",
 | 
			
		||||
		typ,
 | 
			
		||||
		base64.StdEncoding.EncodeToString(salt),
 | 
			
		||||
		base64.StdEncoding.EncodeToString(hash),
 | 
			
		||||
	}, "|")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/hostfile.c#120
 | 
			
		||||
func hashHost(hostname string, salt []byte) []byte {
 | 
			
		||||
	mac := hmac.New(sha1.New, salt)
 | 
			
		||||
	mac.Write([]byte(hostname))
 | 
			
		||||
	return mac.Sum(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type hashedHost struct {
 | 
			
		||||
	salt []byte
 | 
			
		||||
	hash []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const sha1HashType = "1"
 | 
			
		||||
 | 
			
		||||
func newHashedHost(encoded string) (*hashedHost, error) {
 | 
			
		||||
	typ, salt, hash, err := decodeHash(encoded)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The type field seems for future algorithm agility, but it's
 | 
			
		||||
	// actually hardcoded in openssh currently, see
 | 
			
		||||
	// https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/hostfile.c#120
 | 
			
		||||
	if typ != sha1HashType {
 | 
			
		||||
		return nil, fmt.Errorf("knownhosts: got hash type %s, must be '1'", typ)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &hashedHost{salt: salt, hash: hash}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *hashedHost) match(addrs []addr) bool {
 | 
			
		||||
	for _, a := range addrs {
 | 
			
		||||
		if bytes.Equal(hashHost(Normalize(a.String()), h.salt), h.hash) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								vendor/golang.org/x/crypto/ssh/mac.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								vendor/golang.org/x/crypto/ssh/mac.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
// Message authentication support
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/hmac"
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"hash"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type macMode struct {
 | 
			
		||||
	keySize int
 | 
			
		||||
	etm     bool
 | 
			
		||||
	new     func(key []byte) hash.Hash
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// truncatingMAC wraps around a hash.Hash and truncates the output digest to
 | 
			
		||||
// a given size.
 | 
			
		||||
type truncatingMAC struct {
 | 
			
		||||
	length int
 | 
			
		||||
	hmac   hash.Hash
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t truncatingMAC) Write(data []byte) (int, error) {
 | 
			
		||||
	return t.hmac.Write(data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t truncatingMAC) Sum(in []byte) []byte {
 | 
			
		||||
	out := t.hmac.Sum(in)
 | 
			
		||||
	return out[:len(in)+t.length]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t truncatingMAC) Reset() {
 | 
			
		||||
	t.hmac.Reset()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t truncatingMAC) Size() int {
 | 
			
		||||
	return t.length
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() }
 | 
			
		||||
 | 
			
		||||
var macModes = map[string]*macMode{
 | 
			
		||||
	"hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash {
 | 
			
		||||
		return hmac.New(sha256.New, key)
 | 
			
		||||
	}},
 | 
			
		||||
	"hmac-sha2-256": {32, false, func(key []byte) hash.Hash {
 | 
			
		||||
		return hmac.New(sha256.New, key)
 | 
			
		||||
	}},
 | 
			
		||||
	"hmac-sha1": {20, false, func(key []byte) hash.Hash {
 | 
			
		||||
		return hmac.New(sha1.New, key)
 | 
			
		||||
	}},
 | 
			
		||||
	"hmac-sha1-96": {20, false, func(key []byte) hash.Hash {
 | 
			
		||||
		return truncatingMAC{12, hmac.New(sha1.New, key)}
 | 
			
		||||
	}},
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										766
									
								
								vendor/golang.org/x/crypto/ssh/messages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										766
									
								
								vendor/golang.org/x/crypto/ssh/messages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,766 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// These are SSH message type numbers. They are scattered around several
 | 
			
		||||
// documents but many were taken from [SSH-PARAMETERS].
 | 
			
		||||
const (
 | 
			
		||||
	msgIgnore        = 2
 | 
			
		||||
	msgUnimplemented = 3
 | 
			
		||||
	msgDebug         = 4
 | 
			
		||||
	msgNewKeys       = 21
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SSH messages:
 | 
			
		||||
//
 | 
			
		||||
// These structures mirror the wire format of the corresponding SSH messages.
 | 
			
		||||
// They are marshaled using reflection with the marshal and unmarshal functions
 | 
			
		||||
// in this file. The only wrinkle is that a final member of type []byte with a
 | 
			
		||||
// ssh tag of "rest" receives the remainder of a packet when unmarshaling.
 | 
			
		||||
 | 
			
		||||
// See RFC 4253, section 11.1.
 | 
			
		||||
const msgDisconnect = 1
 | 
			
		||||
 | 
			
		||||
// disconnectMsg is the message that signals a disconnect. It is also
 | 
			
		||||
// the error type returned from mux.Wait()
 | 
			
		||||
type disconnectMsg struct {
 | 
			
		||||
	Reason   uint32 `sshtype:"1"`
 | 
			
		||||
	Message  string
 | 
			
		||||
	Language string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *disconnectMsg) Error() string {
 | 
			
		||||
	return fmt.Sprintf("ssh: disconnect, reason %d: %s", d.Reason, d.Message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4253, section 7.1.
 | 
			
		||||
const msgKexInit = 20
 | 
			
		||||
 | 
			
		||||
type kexInitMsg struct {
 | 
			
		||||
	Cookie                  [16]byte `sshtype:"20"`
 | 
			
		||||
	KexAlgos                []string
 | 
			
		||||
	ServerHostKeyAlgos      []string
 | 
			
		||||
	CiphersClientServer     []string
 | 
			
		||||
	CiphersServerClient     []string
 | 
			
		||||
	MACsClientServer        []string
 | 
			
		||||
	MACsServerClient        []string
 | 
			
		||||
	CompressionClientServer []string
 | 
			
		||||
	CompressionServerClient []string
 | 
			
		||||
	LanguagesClientServer   []string
 | 
			
		||||
	LanguagesServerClient   []string
 | 
			
		||||
	FirstKexFollows         bool
 | 
			
		||||
	Reserved                uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4253, section 8.
 | 
			
		||||
 | 
			
		||||
// Diffie-Helman
 | 
			
		||||
const msgKexDHInit = 30
 | 
			
		||||
 | 
			
		||||
type kexDHInitMsg struct {
 | 
			
		||||
	X *big.Int `sshtype:"30"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const msgKexECDHInit = 30
 | 
			
		||||
 | 
			
		||||
type kexECDHInitMsg struct {
 | 
			
		||||
	ClientPubKey []byte `sshtype:"30"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const msgKexECDHReply = 31
 | 
			
		||||
 | 
			
		||||
type kexECDHReplyMsg struct {
 | 
			
		||||
	HostKey         []byte `sshtype:"31"`
 | 
			
		||||
	EphemeralPubKey []byte
 | 
			
		||||
	Signature       []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const msgKexDHReply = 31
 | 
			
		||||
 | 
			
		||||
type kexDHReplyMsg struct {
 | 
			
		||||
	HostKey   []byte `sshtype:"31"`
 | 
			
		||||
	Y         *big.Int
 | 
			
		||||
	Signature []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4253, section 10.
 | 
			
		||||
const msgServiceRequest = 5
 | 
			
		||||
 | 
			
		||||
type serviceRequestMsg struct {
 | 
			
		||||
	Service string `sshtype:"5"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4253, section 10.
 | 
			
		||||
const msgServiceAccept = 6
 | 
			
		||||
 | 
			
		||||
type serviceAcceptMsg struct {
 | 
			
		||||
	Service string `sshtype:"6"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4252, section 5.
 | 
			
		||||
const msgUserAuthRequest = 50
 | 
			
		||||
 | 
			
		||||
type userAuthRequestMsg struct {
 | 
			
		||||
	User    string `sshtype:"50"`
 | 
			
		||||
	Service string
 | 
			
		||||
	Method  string
 | 
			
		||||
	Payload []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Used for debug printouts of packets.
 | 
			
		||||
type userAuthSuccessMsg struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4252, section 5.1
 | 
			
		||||
const msgUserAuthFailure = 51
 | 
			
		||||
 | 
			
		||||
type userAuthFailureMsg struct {
 | 
			
		||||
	Methods        []string `sshtype:"51"`
 | 
			
		||||
	PartialSuccess bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4252, section 5.1
 | 
			
		||||
const msgUserAuthSuccess = 52
 | 
			
		||||
 | 
			
		||||
// See RFC 4252, section 5.4
 | 
			
		||||
const msgUserAuthBanner = 53
 | 
			
		||||
 | 
			
		||||
type userAuthBannerMsg struct {
 | 
			
		||||
	Message string `sshtype:"53"`
 | 
			
		||||
	// unused, but required to allow message parsing
 | 
			
		||||
	Language string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4256, section 3.2
 | 
			
		||||
const msgUserAuthInfoRequest = 60
 | 
			
		||||
const msgUserAuthInfoResponse = 61
 | 
			
		||||
 | 
			
		||||
type userAuthInfoRequestMsg struct {
 | 
			
		||||
	User               string `sshtype:"60"`
 | 
			
		||||
	Instruction        string
 | 
			
		||||
	DeprecatedLanguage string
 | 
			
		||||
	NumPrompts         uint32
 | 
			
		||||
	Prompts            []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4254, section 5.1.
 | 
			
		||||
const msgChannelOpen = 90
 | 
			
		||||
 | 
			
		||||
type channelOpenMsg struct {
 | 
			
		||||
	ChanType         string `sshtype:"90"`
 | 
			
		||||
	PeersID          uint32
 | 
			
		||||
	PeersWindow      uint32
 | 
			
		||||
	MaxPacketSize    uint32
 | 
			
		||||
	TypeSpecificData []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const msgChannelExtendedData = 95
 | 
			
		||||
const msgChannelData = 94
 | 
			
		||||
 | 
			
		||||
// Used for debug print outs of packets.
 | 
			
		||||
type channelDataMsg struct {
 | 
			
		||||
	PeersID uint32 `sshtype:"94"`
 | 
			
		||||
	Length  uint32
 | 
			
		||||
	Rest    []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4254, section 5.1.
 | 
			
		||||
const msgChannelOpenConfirm = 91
 | 
			
		||||
 | 
			
		||||
type channelOpenConfirmMsg struct {
 | 
			
		||||
	PeersID          uint32 `sshtype:"91"`
 | 
			
		||||
	MyID             uint32
 | 
			
		||||
	MyWindow         uint32
 | 
			
		||||
	MaxPacketSize    uint32
 | 
			
		||||
	TypeSpecificData []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4254, section 5.1.
 | 
			
		||||
const msgChannelOpenFailure = 92
 | 
			
		||||
 | 
			
		||||
type channelOpenFailureMsg struct {
 | 
			
		||||
	PeersID  uint32 `sshtype:"92"`
 | 
			
		||||
	Reason   RejectionReason
 | 
			
		||||
	Message  string
 | 
			
		||||
	Language string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const msgChannelRequest = 98
 | 
			
		||||
 | 
			
		||||
type channelRequestMsg struct {
 | 
			
		||||
	PeersID             uint32 `sshtype:"98"`
 | 
			
		||||
	Request             string
 | 
			
		||||
	WantReply           bool
 | 
			
		||||
	RequestSpecificData []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4254, section 5.4.
 | 
			
		||||
const msgChannelSuccess = 99
 | 
			
		||||
 | 
			
		||||
type channelRequestSuccessMsg struct {
 | 
			
		||||
	PeersID uint32 `sshtype:"99"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4254, section 5.4.
 | 
			
		||||
const msgChannelFailure = 100
 | 
			
		||||
 | 
			
		||||
type channelRequestFailureMsg struct {
 | 
			
		||||
	PeersID uint32 `sshtype:"100"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4254, section 5.3
 | 
			
		||||
const msgChannelClose = 97
 | 
			
		||||
 | 
			
		||||
type channelCloseMsg struct {
 | 
			
		||||
	PeersID uint32 `sshtype:"97"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4254, section 5.3
 | 
			
		||||
const msgChannelEOF = 96
 | 
			
		||||
 | 
			
		||||
type channelEOFMsg struct {
 | 
			
		||||
	PeersID uint32 `sshtype:"96"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4254, section 4
 | 
			
		||||
const msgGlobalRequest = 80
 | 
			
		||||
 | 
			
		||||
type globalRequestMsg struct {
 | 
			
		||||
	Type      string `sshtype:"80"`
 | 
			
		||||
	WantReply bool
 | 
			
		||||
	Data      []byte `ssh:"rest"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4254, section 4
 | 
			
		||||
const msgRequestSuccess = 81
 | 
			
		||||
 | 
			
		||||
type globalRequestSuccessMsg struct {
 | 
			
		||||
	Data []byte `ssh:"rest" sshtype:"81"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4254, section 4
 | 
			
		||||
const msgRequestFailure = 82
 | 
			
		||||
 | 
			
		||||
type globalRequestFailureMsg struct {
 | 
			
		||||
	Data []byte `ssh:"rest" sshtype:"82"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4254, section 5.2
 | 
			
		||||
const msgChannelWindowAdjust = 93
 | 
			
		||||
 | 
			
		||||
type windowAdjustMsg struct {
 | 
			
		||||
	PeersID         uint32 `sshtype:"93"`
 | 
			
		||||
	AdditionalBytes uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4252, section 7
 | 
			
		||||
const msgUserAuthPubKeyOk = 60
 | 
			
		||||
 | 
			
		||||
type userAuthPubKeyOkMsg struct {
 | 
			
		||||
	Algo   string `sshtype:"60"`
 | 
			
		||||
	PubKey []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// typeTags returns the possible type bytes for the given reflect.Type, which
 | 
			
		||||
// should be a struct. The possible values are separated by a '|' character.
 | 
			
		||||
func typeTags(structType reflect.Type) (tags []byte) {
 | 
			
		||||
	tagStr := structType.Field(0).Tag.Get("sshtype")
 | 
			
		||||
 | 
			
		||||
	for _, tag := range strings.Split(tagStr, "|") {
 | 
			
		||||
		i, err := strconv.Atoi(tag)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			tags = append(tags, byte(i))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return tags
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fieldError(t reflect.Type, field int, problem string) error {
 | 
			
		||||
	if problem != "" {
 | 
			
		||||
		problem = ": " + problem
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("ssh: unmarshal error for field %s of type %s%s", t.Field(field).Name, t.Name(), problem)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errShortRead = errors.New("ssh: short read")
 | 
			
		||||
 | 
			
		||||
// Unmarshal parses data in SSH wire format into a structure. The out
 | 
			
		||||
// argument should be a pointer to struct. If the first member of the
 | 
			
		||||
// struct has the "sshtype" tag set to a '|'-separated set of numbers
 | 
			
		||||
// in decimal, the packet must start with one of those numbers. In
 | 
			
		||||
// case of error, Unmarshal returns a ParseError or
 | 
			
		||||
// UnexpectedMessageError.
 | 
			
		||||
func Unmarshal(data []byte, out interface{}) error {
 | 
			
		||||
	v := reflect.ValueOf(out).Elem()
 | 
			
		||||
	structType := v.Type()
 | 
			
		||||
	expectedTypes := typeTags(structType)
 | 
			
		||||
 | 
			
		||||
	var expectedType byte
 | 
			
		||||
	if len(expectedTypes) > 0 {
 | 
			
		||||
		expectedType = expectedTypes[0]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(data) == 0 {
 | 
			
		||||
		return parseError(expectedType)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(expectedTypes) > 0 {
 | 
			
		||||
		goodType := false
 | 
			
		||||
		for _, e := range expectedTypes {
 | 
			
		||||
			if e > 0 && data[0] == e {
 | 
			
		||||
				goodType = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !goodType {
 | 
			
		||||
			return fmt.Errorf("ssh: unexpected message type %d (expected one of %v)", data[0], expectedTypes)
 | 
			
		||||
		}
 | 
			
		||||
		data = data[1:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var ok bool
 | 
			
		||||
	for i := 0; i < v.NumField(); i++ {
 | 
			
		||||
		field := v.Field(i)
 | 
			
		||||
		t := field.Type()
 | 
			
		||||
		switch t.Kind() {
 | 
			
		||||
		case reflect.Bool:
 | 
			
		||||
			if len(data) < 1 {
 | 
			
		||||
				return errShortRead
 | 
			
		||||
			}
 | 
			
		||||
			field.SetBool(data[0] != 0)
 | 
			
		||||
			data = data[1:]
 | 
			
		||||
		case reflect.Array:
 | 
			
		||||
			if t.Elem().Kind() != reflect.Uint8 {
 | 
			
		||||
				return fieldError(structType, i, "array of unsupported type")
 | 
			
		||||
			}
 | 
			
		||||
			if len(data) < t.Len() {
 | 
			
		||||
				return errShortRead
 | 
			
		||||
			}
 | 
			
		||||
			for j, n := 0, t.Len(); j < n; j++ {
 | 
			
		||||
				field.Index(j).Set(reflect.ValueOf(data[j]))
 | 
			
		||||
			}
 | 
			
		||||
			data = data[t.Len():]
 | 
			
		||||
		case reflect.Uint64:
 | 
			
		||||
			var u64 uint64
 | 
			
		||||
			if u64, data, ok = parseUint64(data); !ok {
 | 
			
		||||
				return errShortRead
 | 
			
		||||
			}
 | 
			
		||||
			field.SetUint(u64)
 | 
			
		||||
		case reflect.Uint32:
 | 
			
		||||
			var u32 uint32
 | 
			
		||||
			if u32, data, ok = parseUint32(data); !ok {
 | 
			
		||||
				return errShortRead
 | 
			
		||||
			}
 | 
			
		||||
			field.SetUint(uint64(u32))
 | 
			
		||||
		case reflect.Uint8:
 | 
			
		||||
			if len(data) < 1 {
 | 
			
		||||
				return errShortRead
 | 
			
		||||
			}
 | 
			
		||||
			field.SetUint(uint64(data[0]))
 | 
			
		||||
			data = data[1:]
 | 
			
		||||
		case reflect.String:
 | 
			
		||||
			var s []byte
 | 
			
		||||
			if s, data, ok = parseString(data); !ok {
 | 
			
		||||
				return fieldError(structType, i, "")
 | 
			
		||||
			}
 | 
			
		||||
			field.SetString(string(s))
 | 
			
		||||
		case reflect.Slice:
 | 
			
		||||
			switch t.Elem().Kind() {
 | 
			
		||||
			case reflect.Uint8:
 | 
			
		||||
				if structType.Field(i).Tag.Get("ssh") == "rest" {
 | 
			
		||||
					field.Set(reflect.ValueOf(data))
 | 
			
		||||
					data = nil
 | 
			
		||||
				} else {
 | 
			
		||||
					var s []byte
 | 
			
		||||
					if s, data, ok = parseString(data); !ok {
 | 
			
		||||
						return errShortRead
 | 
			
		||||
					}
 | 
			
		||||
					field.Set(reflect.ValueOf(s))
 | 
			
		||||
				}
 | 
			
		||||
			case reflect.String:
 | 
			
		||||
				var nl []string
 | 
			
		||||
				if nl, data, ok = parseNameList(data); !ok {
 | 
			
		||||
					return errShortRead
 | 
			
		||||
				}
 | 
			
		||||
				field.Set(reflect.ValueOf(nl))
 | 
			
		||||
			default:
 | 
			
		||||
				return fieldError(structType, i, "slice of unsupported type")
 | 
			
		||||
			}
 | 
			
		||||
		case reflect.Ptr:
 | 
			
		||||
			if t == bigIntType {
 | 
			
		||||
				var n *big.Int
 | 
			
		||||
				if n, data, ok = parseInt(data); !ok {
 | 
			
		||||
					return errShortRead
 | 
			
		||||
				}
 | 
			
		||||
				field.Set(reflect.ValueOf(n))
 | 
			
		||||
			} else {
 | 
			
		||||
				return fieldError(structType, i, "pointer to unsupported type")
 | 
			
		||||
			}
 | 
			
		||||
		default:
 | 
			
		||||
			return fieldError(structType, i, fmt.Sprintf("unsupported type: %v", t))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(data) != 0 {
 | 
			
		||||
		return parseError(expectedType)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Marshal serializes the message in msg to SSH wire format.  The msg
 | 
			
		||||
// argument should be a struct or pointer to struct. If the first
 | 
			
		||||
// member has the "sshtype" tag set to a number in decimal, that
 | 
			
		||||
// number is prepended to the result. If the last of member has the
 | 
			
		||||
// "ssh" tag set to "rest", its contents are appended to the output.
 | 
			
		||||
func Marshal(msg interface{}) []byte {
 | 
			
		||||
	out := make([]byte, 0, 64)
 | 
			
		||||
	return marshalStruct(out, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalStruct(out []byte, msg interface{}) []byte {
 | 
			
		||||
	v := reflect.Indirect(reflect.ValueOf(msg))
 | 
			
		||||
	msgTypes := typeTags(v.Type())
 | 
			
		||||
	if len(msgTypes) > 0 {
 | 
			
		||||
		out = append(out, msgTypes[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, n := 0, v.NumField(); i < n; i++ {
 | 
			
		||||
		field := v.Field(i)
 | 
			
		||||
		switch t := field.Type(); t.Kind() {
 | 
			
		||||
		case reflect.Bool:
 | 
			
		||||
			var v uint8
 | 
			
		||||
			if field.Bool() {
 | 
			
		||||
				v = 1
 | 
			
		||||
			}
 | 
			
		||||
			out = append(out, v)
 | 
			
		||||
		case reflect.Array:
 | 
			
		||||
			if t.Elem().Kind() != reflect.Uint8 {
 | 
			
		||||
				panic(fmt.Sprintf("array of non-uint8 in field %d: %T", i, field.Interface()))
 | 
			
		||||
			}
 | 
			
		||||
			for j, l := 0, t.Len(); j < l; j++ {
 | 
			
		||||
				out = append(out, uint8(field.Index(j).Uint()))
 | 
			
		||||
			}
 | 
			
		||||
		case reflect.Uint32:
 | 
			
		||||
			out = appendU32(out, uint32(field.Uint()))
 | 
			
		||||
		case reflect.Uint64:
 | 
			
		||||
			out = appendU64(out, uint64(field.Uint()))
 | 
			
		||||
		case reflect.Uint8:
 | 
			
		||||
			out = append(out, uint8(field.Uint()))
 | 
			
		||||
		case reflect.String:
 | 
			
		||||
			s := field.String()
 | 
			
		||||
			out = appendInt(out, len(s))
 | 
			
		||||
			out = append(out, s...)
 | 
			
		||||
		case reflect.Slice:
 | 
			
		||||
			switch t.Elem().Kind() {
 | 
			
		||||
			case reflect.Uint8:
 | 
			
		||||
				if v.Type().Field(i).Tag.Get("ssh") != "rest" {
 | 
			
		||||
					out = appendInt(out, field.Len())
 | 
			
		||||
				}
 | 
			
		||||
				out = append(out, field.Bytes()...)
 | 
			
		||||
			case reflect.String:
 | 
			
		||||
				offset := len(out)
 | 
			
		||||
				out = appendU32(out, 0)
 | 
			
		||||
				if n := field.Len(); n > 0 {
 | 
			
		||||
					for j := 0; j < n; j++ {
 | 
			
		||||
						f := field.Index(j)
 | 
			
		||||
						if j != 0 {
 | 
			
		||||
							out = append(out, ',')
 | 
			
		||||
						}
 | 
			
		||||
						out = append(out, f.String()...)
 | 
			
		||||
					}
 | 
			
		||||
					// overwrite length value
 | 
			
		||||
					binary.BigEndian.PutUint32(out[offset:], uint32(len(out)-offset-4))
 | 
			
		||||
				}
 | 
			
		||||
			default:
 | 
			
		||||
				panic(fmt.Sprintf("slice of unknown type in field %d: %T", i, field.Interface()))
 | 
			
		||||
			}
 | 
			
		||||
		case reflect.Ptr:
 | 
			
		||||
			if t == bigIntType {
 | 
			
		||||
				var n *big.Int
 | 
			
		||||
				nValue := reflect.ValueOf(&n)
 | 
			
		||||
				nValue.Elem().Set(field)
 | 
			
		||||
				needed := intLength(n)
 | 
			
		||||
				oldLength := len(out)
 | 
			
		||||
 | 
			
		||||
				if cap(out)-len(out) < needed {
 | 
			
		||||
					newOut := make([]byte, len(out), 2*(len(out)+needed))
 | 
			
		||||
					copy(newOut, out)
 | 
			
		||||
					out = newOut
 | 
			
		||||
				}
 | 
			
		||||
				out = out[:oldLength+needed]
 | 
			
		||||
				marshalInt(out[oldLength:], n)
 | 
			
		||||
			} else {
 | 
			
		||||
				panic(fmt.Sprintf("pointer to unknown type in field %d: %T", i, field.Interface()))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var bigOne = big.NewInt(1)
 | 
			
		||||
 | 
			
		||||
func parseString(in []byte) (out, rest []byte, ok bool) {
 | 
			
		||||
	if len(in) < 4 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	length := binary.BigEndian.Uint32(in)
 | 
			
		||||
	in = in[4:]
 | 
			
		||||
	if uint32(len(in)) < length {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	out = in[:length]
 | 
			
		||||
	rest = in[length:]
 | 
			
		||||
	ok = true
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	comma         = []byte{','}
 | 
			
		||||
	emptyNameList = []string{}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func parseNameList(in []byte) (out []string, rest []byte, ok bool) {
 | 
			
		||||
	contents, rest, ok := parseString(in)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(contents) == 0 {
 | 
			
		||||
		out = emptyNameList
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	parts := bytes.Split(contents, comma)
 | 
			
		||||
	out = make([]string, len(parts))
 | 
			
		||||
	for i, part := range parts {
 | 
			
		||||
		out[i] = string(part)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseInt(in []byte) (out *big.Int, rest []byte, ok bool) {
 | 
			
		||||
	contents, rest, ok := parseString(in)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	out = new(big.Int)
 | 
			
		||||
 | 
			
		||||
	if len(contents) > 0 && contents[0]&0x80 == 0x80 {
 | 
			
		||||
		// This is a negative number
 | 
			
		||||
		notBytes := make([]byte, len(contents))
 | 
			
		||||
		for i := range notBytes {
 | 
			
		||||
			notBytes[i] = ^contents[i]
 | 
			
		||||
		}
 | 
			
		||||
		out.SetBytes(notBytes)
 | 
			
		||||
		out.Add(out, bigOne)
 | 
			
		||||
		out.Neg(out)
 | 
			
		||||
	} else {
 | 
			
		||||
		// Positive number
 | 
			
		||||
		out.SetBytes(contents)
 | 
			
		||||
	}
 | 
			
		||||
	ok = true
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseUint32(in []byte) (uint32, []byte, bool) {
 | 
			
		||||
	if len(in) < 4 {
 | 
			
		||||
		return 0, nil, false
 | 
			
		||||
	}
 | 
			
		||||
	return binary.BigEndian.Uint32(in), in[4:], true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseUint64(in []byte) (uint64, []byte, bool) {
 | 
			
		||||
	if len(in) < 8 {
 | 
			
		||||
		return 0, nil, false
 | 
			
		||||
	}
 | 
			
		||||
	return binary.BigEndian.Uint64(in), in[8:], true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func intLength(n *big.Int) int {
 | 
			
		||||
	length := 4 /* length bytes */
 | 
			
		||||
	if n.Sign() < 0 {
 | 
			
		||||
		nMinus1 := new(big.Int).Neg(n)
 | 
			
		||||
		nMinus1.Sub(nMinus1, bigOne)
 | 
			
		||||
		bitLen := nMinus1.BitLen()
 | 
			
		||||
		if bitLen%8 == 0 {
 | 
			
		||||
			// The number will need 0xff padding
 | 
			
		||||
			length++
 | 
			
		||||
		}
 | 
			
		||||
		length += (bitLen + 7) / 8
 | 
			
		||||
	} else if n.Sign() == 0 {
 | 
			
		||||
		// A zero is the zero length string
 | 
			
		||||
	} else {
 | 
			
		||||
		bitLen := n.BitLen()
 | 
			
		||||
		if bitLen%8 == 0 {
 | 
			
		||||
			// The number will need 0x00 padding
 | 
			
		||||
			length++
 | 
			
		||||
		}
 | 
			
		||||
		length += (bitLen + 7) / 8
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return length
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalUint32(to []byte, n uint32) []byte {
 | 
			
		||||
	binary.BigEndian.PutUint32(to, n)
 | 
			
		||||
	return to[4:]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalUint64(to []byte, n uint64) []byte {
 | 
			
		||||
	binary.BigEndian.PutUint64(to, n)
 | 
			
		||||
	return to[8:]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalInt(to []byte, n *big.Int) []byte {
 | 
			
		||||
	lengthBytes := to
 | 
			
		||||
	to = to[4:]
 | 
			
		||||
	length := 0
 | 
			
		||||
 | 
			
		||||
	if n.Sign() < 0 {
 | 
			
		||||
		// A negative number has to be converted to two's-complement
 | 
			
		||||
		// form. So we'll subtract 1 and invert. If the
 | 
			
		||||
		// most-significant-bit isn't set then we'll need to pad the
 | 
			
		||||
		// beginning with 0xff in order to keep the number negative.
 | 
			
		||||
		nMinus1 := new(big.Int).Neg(n)
 | 
			
		||||
		nMinus1.Sub(nMinus1, bigOne)
 | 
			
		||||
		bytes := nMinus1.Bytes()
 | 
			
		||||
		for i := range bytes {
 | 
			
		||||
			bytes[i] ^= 0xff
 | 
			
		||||
		}
 | 
			
		||||
		if len(bytes) == 0 || bytes[0]&0x80 == 0 {
 | 
			
		||||
			to[0] = 0xff
 | 
			
		||||
			to = to[1:]
 | 
			
		||||
			length++
 | 
			
		||||
		}
 | 
			
		||||
		nBytes := copy(to, bytes)
 | 
			
		||||
		to = to[nBytes:]
 | 
			
		||||
		length += nBytes
 | 
			
		||||
	} else if n.Sign() == 0 {
 | 
			
		||||
		// A zero is the zero length string
 | 
			
		||||
	} else {
 | 
			
		||||
		bytes := n.Bytes()
 | 
			
		||||
		if len(bytes) > 0 && bytes[0]&0x80 != 0 {
 | 
			
		||||
			// We'll have to pad this with a 0x00 in order to
 | 
			
		||||
			// stop it looking like a negative number.
 | 
			
		||||
			to[0] = 0
 | 
			
		||||
			to = to[1:]
 | 
			
		||||
			length++
 | 
			
		||||
		}
 | 
			
		||||
		nBytes := copy(to, bytes)
 | 
			
		||||
		to = to[nBytes:]
 | 
			
		||||
		length += nBytes
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lengthBytes[0] = byte(length >> 24)
 | 
			
		||||
	lengthBytes[1] = byte(length >> 16)
 | 
			
		||||
	lengthBytes[2] = byte(length >> 8)
 | 
			
		||||
	lengthBytes[3] = byte(length)
 | 
			
		||||
	return to
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeInt(w io.Writer, n *big.Int) {
 | 
			
		||||
	length := intLength(n)
 | 
			
		||||
	buf := make([]byte, length)
 | 
			
		||||
	marshalInt(buf, n)
 | 
			
		||||
	w.Write(buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeString(w io.Writer, s []byte) {
 | 
			
		||||
	var lengthBytes [4]byte
 | 
			
		||||
	lengthBytes[0] = byte(len(s) >> 24)
 | 
			
		||||
	lengthBytes[1] = byte(len(s) >> 16)
 | 
			
		||||
	lengthBytes[2] = byte(len(s) >> 8)
 | 
			
		||||
	lengthBytes[3] = byte(len(s))
 | 
			
		||||
	w.Write(lengthBytes[:])
 | 
			
		||||
	w.Write(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func stringLength(n int) int {
 | 
			
		||||
	return 4 + n
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalString(to []byte, s []byte) []byte {
 | 
			
		||||
	to[0] = byte(len(s) >> 24)
 | 
			
		||||
	to[1] = byte(len(s) >> 16)
 | 
			
		||||
	to[2] = byte(len(s) >> 8)
 | 
			
		||||
	to[3] = byte(len(s))
 | 
			
		||||
	to = to[4:]
 | 
			
		||||
	copy(to, s)
 | 
			
		||||
	return to[len(s):]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var bigIntType = reflect.TypeOf((*big.Int)(nil))
 | 
			
		||||
 | 
			
		||||
// Decode a packet into its corresponding message.
 | 
			
		||||
func decode(packet []byte) (interface{}, error) {
 | 
			
		||||
	var msg interface{}
 | 
			
		||||
	switch packet[0] {
 | 
			
		||||
	case msgDisconnect:
 | 
			
		||||
		msg = new(disconnectMsg)
 | 
			
		||||
	case msgServiceRequest:
 | 
			
		||||
		msg = new(serviceRequestMsg)
 | 
			
		||||
	case msgServiceAccept:
 | 
			
		||||
		msg = new(serviceAcceptMsg)
 | 
			
		||||
	case msgKexInit:
 | 
			
		||||
		msg = new(kexInitMsg)
 | 
			
		||||
	case msgKexDHInit:
 | 
			
		||||
		msg = new(kexDHInitMsg)
 | 
			
		||||
	case msgKexDHReply:
 | 
			
		||||
		msg = new(kexDHReplyMsg)
 | 
			
		||||
	case msgUserAuthRequest:
 | 
			
		||||
		msg = new(userAuthRequestMsg)
 | 
			
		||||
	case msgUserAuthSuccess:
 | 
			
		||||
		return new(userAuthSuccessMsg), nil
 | 
			
		||||
	case msgUserAuthFailure:
 | 
			
		||||
		msg = new(userAuthFailureMsg)
 | 
			
		||||
	case msgUserAuthPubKeyOk:
 | 
			
		||||
		msg = new(userAuthPubKeyOkMsg)
 | 
			
		||||
	case msgGlobalRequest:
 | 
			
		||||
		msg = new(globalRequestMsg)
 | 
			
		||||
	case msgRequestSuccess:
 | 
			
		||||
		msg = new(globalRequestSuccessMsg)
 | 
			
		||||
	case msgRequestFailure:
 | 
			
		||||
		msg = new(globalRequestFailureMsg)
 | 
			
		||||
	case msgChannelOpen:
 | 
			
		||||
		msg = new(channelOpenMsg)
 | 
			
		||||
	case msgChannelData:
 | 
			
		||||
		msg = new(channelDataMsg)
 | 
			
		||||
	case msgChannelOpenConfirm:
 | 
			
		||||
		msg = new(channelOpenConfirmMsg)
 | 
			
		||||
	case msgChannelOpenFailure:
 | 
			
		||||
		msg = new(channelOpenFailureMsg)
 | 
			
		||||
	case msgChannelWindowAdjust:
 | 
			
		||||
		msg = new(windowAdjustMsg)
 | 
			
		||||
	case msgChannelEOF:
 | 
			
		||||
		msg = new(channelEOFMsg)
 | 
			
		||||
	case msgChannelClose:
 | 
			
		||||
		msg = new(channelCloseMsg)
 | 
			
		||||
	case msgChannelRequest:
 | 
			
		||||
		msg = new(channelRequestMsg)
 | 
			
		||||
	case msgChannelSuccess:
 | 
			
		||||
		msg = new(channelRequestSuccessMsg)
 | 
			
		||||
	case msgChannelFailure:
 | 
			
		||||
		msg = new(channelRequestFailureMsg)
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, unexpectedMessageError(0, packet[0])
 | 
			
		||||
	}
 | 
			
		||||
	if err := Unmarshal(packet, msg); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return msg, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										330
									
								
								vendor/golang.org/x/crypto/ssh/mux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										330
									
								
								vendor/golang.org/x/crypto/ssh/mux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,330 @@
 | 
			
		||||
// Copyright 2013 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"sync/atomic"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// debugMux, if set, causes messages in the connection protocol to be
 | 
			
		||||
// logged.
 | 
			
		||||
const debugMux = false
 | 
			
		||||
 | 
			
		||||
// chanList is a thread safe channel list.
 | 
			
		||||
type chanList struct {
 | 
			
		||||
	// protects concurrent access to chans
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
 | 
			
		||||
	// chans are indexed by the local id of the channel, which the
 | 
			
		||||
	// other side should send in the PeersId field.
 | 
			
		||||
	chans []*channel
 | 
			
		||||
 | 
			
		||||
	// This is a debugging aid: it offsets all IDs by this
 | 
			
		||||
	// amount. This helps distinguish otherwise identical
 | 
			
		||||
	// server/client muxes
 | 
			
		||||
	offset uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Assigns a channel ID to the given channel.
 | 
			
		||||
func (c *chanList) add(ch *channel) uint32 {
 | 
			
		||||
	c.Lock()
 | 
			
		||||
	defer c.Unlock()
 | 
			
		||||
	for i := range c.chans {
 | 
			
		||||
		if c.chans[i] == nil {
 | 
			
		||||
			c.chans[i] = ch
 | 
			
		||||
			return uint32(i) + c.offset
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	c.chans = append(c.chans, ch)
 | 
			
		||||
	return uint32(len(c.chans)-1) + c.offset
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getChan returns the channel for the given ID.
 | 
			
		||||
func (c *chanList) getChan(id uint32) *channel {
 | 
			
		||||
	id -= c.offset
 | 
			
		||||
 | 
			
		||||
	c.Lock()
 | 
			
		||||
	defer c.Unlock()
 | 
			
		||||
	if id < uint32(len(c.chans)) {
 | 
			
		||||
		return c.chans[id]
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *chanList) remove(id uint32) {
 | 
			
		||||
	id -= c.offset
 | 
			
		||||
	c.Lock()
 | 
			
		||||
	if id < uint32(len(c.chans)) {
 | 
			
		||||
		c.chans[id] = nil
 | 
			
		||||
	}
 | 
			
		||||
	c.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dropAll forgets all channels it knows, returning them in a slice.
 | 
			
		||||
func (c *chanList) dropAll() []*channel {
 | 
			
		||||
	c.Lock()
 | 
			
		||||
	defer c.Unlock()
 | 
			
		||||
	var r []*channel
 | 
			
		||||
 | 
			
		||||
	for _, ch := range c.chans {
 | 
			
		||||
		if ch == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		r = append(r, ch)
 | 
			
		||||
	}
 | 
			
		||||
	c.chans = nil
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// mux represents the state for the SSH connection protocol, which
 | 
			
		||||
// multiplexes many channels onto a single packet transport.
 | 
			
		||||
type mux struct {
 | 
			
		||||
	conn     packetConn
 | 
			
		||||
	chanList chanList
 | 
			
		||||
 | 
			
		||||
	incomingChannels chan NewChannel
 | 
			
		||||
 | 
			
		||||
	globalSentMu     sync.Mutex
 | 
			
		||||
	globalResponses  chan interface{}
 | 
			
		||||
	incomingRequests chan *Request
 | 
			
		||||
 | 
			
		||||
	errCond *sync.Cond
 | 
			
		||||
	err     error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// When debugging, each new chanList instantiation has a different
 | 
			
		||||
// offset.
 | 
			
		||||
var globalOff uint32
 | 
			
		||||
 | 
			
		||||
func (m *mux) Wait() error {
 | 
			
		||||
	m.errCond.L.Lock()
 | 
			
		||||
	defer m.errCond.L.Unlock()
 | 
			
		||||
	for m.err == nil {
 | 
			
		||||
		m.errCond.Wait()
 | 
			
		||||
	}
 | 
			
		||||
	return m.err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newMux returns a mux that runs over the given connection.
 | 
			
		||||
func newMux(p packetConn) *mux {
 | 
			
		||||
	m := &mux{
 | 
			
		||||
		conn:             p,
 | 
			
		||||
		incomingChannels: make(chan NewChannel, chanSize),
 | 
			
		||||
		globalResponses:  make(chan interface{}, 1),
 | 
			
		||||
		incomingRequests: make(chan *Request, chanSize),
 | 
			
		||||
		errCond:          newCond(),
 | 
			
		||||
	}
 | 
			
		||||
	if debugMux {
 | 
			
		||||
		m.chanList.offset = atomic.AddUint32(&globalOff, 1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go m.loop()
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *mux) sendMessage(msg interface{}) error {
 | 
			
		||||
	p := Marshal(msg)
 | 
			
		||||
	if debugMux {
 | 
			
		||||
		log.Printf("send global(%d): %#v", m.chanList.offset, msg)
 | 
			
		||||
	}
 | 
			
		||||
	return m.conn.writePacket(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *mux) SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) {
 | 
			
		||||
	if wantReply {
 | 
			
		||||
		m.globalSentMu.Lock()
 | 
			
		||||
		defer m.globalSentMu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := m.sendMessage(globalRequestMsg{
 | 
			
		||||
		Type:      name,
 | 
			
		||||
		WantReply: wantReply,
 | 
			
		||||
		Data:      payload,
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return false, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !wantReply {
 | 
			
		||||
		return false, nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg, ok := <-m.globalResponses
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return false, nil, io.EOF
 | 
			
		||||
	}
 | 
			
		||||
	switch msg := msg.(type) {
 | 
			
		||||
	case *globalRequestFailureMsg:
 | 
			
		||||
		return false, msg.Data, nil
 | 
			
		||||
	case *globalRequestSuccessMsg:
 | 
			
		||||
		return true, msg.Data, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return false, nil, fmt.Errorf("ssh: unexpected response to request: %#v", msg)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ackRequest must be called after processing a global request that
 | 
			
		||||
// has WantReply set.
 | 
			
		||||
func (m *mux) ackRequest(ok bool, data []byte) error {
 | 
			
		||||
	if ok {
 | 
			
		||||
		return m.sendMessage(globalRequestSuccessMsg{Data: data})
 | 
			
		||||
	}
 | 
			
		||||
	return m.sendMessage(globalRequestFailureMsg{Data: data})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *mux) Close() error {
 | 
			
		||||
	return m.conn.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// loop runs the connection machine. It will process packets until an
 | 
			
		||||
// error is encountered. To synchronize on loop exit, use mux.Wait.
 | 
			
		||||
func (m *mux) loop() {
 | 
			
		||||
	var err error
 | 
			
		||||
	for err == nil {
 | 
			
		||||
		err = m.onePacket()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, ch := range m.chanList.dropAll() {
 | 
			
		||||
		ch.close()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	close(m.incomingChannels)
 | 
			
		||||
	close(m.incomingRequests)
 | 
			
		||||
	close(m.globalResponses)
 | 
			
		||||
 | 
			
		||||
	m.conn.Close()
 | 
			
		||||
 | 
			
		||||
	m.errCond.L.Lock()
 | 
			
		||||
	m.err = err
 | 
			
		||||
	m.errCond.Broadcast()
 | 
			
		||||
	m.errCond.L.Unlock()
 | 
			
		||||
 | 
			
		||||
	if debugMux {
 | 
			
		||||
		log.Println("loop exit", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// onePacket reads and processes one packet.
 | 
			
		||||
func (m *mux) onePacket() error {
 | 
			
		||||
	packet, err := m.conn.readPacket()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if debugMux {
 | 
			
		||||
		if packet[0] == msgChannelData || packet[0] == msgChannelExtendedData {
 | 
			
		||||
			log.Printf("decoding(%d): data packet - %d bytes", m.chanList.offset, len(packet))
 | 
			
		||||
		} else {
 | 
			
		||||
			p, _ := decode(packet)
 | 
			
		||||
			log.Printf("decoding(%d): %d %#v - %d bytes", m.chanList.offset, packet[0], p, len(packet))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch packet[0] {
 | 
			
		||||
	case msgChannelOpen:
 | 
			
		||||
		return m.handleChannelOpen(packet)
 | 
			
		||||
	case msgGlobalRequest, msgRequestSuccess, msgRequestFailure:
 | 
			
		||||
		return m.handleGlobalPacket(packet)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// assume a channel packet.
 | 
			
		||||
	if len(packet) < 5 {
 | 
			
		||||
		return parseError(packet[0])
 | 
			
		||||
	}
 | 
			
		||||
	id := binary.BigEndian.Uint32(packet[1:])
 | 
			
		||||
	ch := m.chanList.getChan(id)
 | 
			
		||||
	if ch == nil {
 | 
			
		||||
		return fmt.Errorf("ssh: invalid channel %d", id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ch.handlePacket(packet)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *mux) handleGlobalPacket(packet []byte) error {
 | 
			
		||||
	msg, err := decode(packet)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch msg := msg.(type) {
 | 
			
		||||
	case *globalRequestMsg:
 | 
			
		||||
		m.incomingRequests <- &Request{
 | 
			
		||||
			Type:      msg.Type,
 | 
			
		||||
			WantReply: msg.WantReply,
 | 
			
		||||
			Payload:   msg.Data,
 | 
			
		||||
			mux:       m,
 | 
			
		||||
		}
 | 
			
		||||
	case *globalRequestSuccessMsg, *globalRequestFailureMsg:
 | 
			
		||||
		m.globalResponses <- msg
 | 
			
		||||
	default:
 | 
			
		||||
		panic(fmt.Sprintf("not a global message %#v", msg))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleChannelOpen schedules a channel to be Accept()ed.
 | 
			
		||||
func (m *mux) handleChannelOpen(packet []byte) error {
 | 
			
		||||
	var msg channelOpenMsg
 | 
			
		||||
	if err := Unmarshal(packet, &msg); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 {
 | 
			
		||||
		failMsg := channelOpenFailureMsg{
 | 
			
		||||
			PeersID:  msg.PeersID,
 | 
			
		||||
			Reason:   ConnectionFailed,
 | 
			
		||||
			Message:  "invalid request",
 | 
			
		||||
			Language: "en_US.UTF-8",
 | 
			
		||||
		}
 | 
			
		||||
		return m.sendMessage(failMsg)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := m.newChannel(msg.ChanType, channelInbound, msg.TypeSpecificData)
 | 
			
		||||
	c.remoteId = msg.PeersID
 | 
			
		||||
	c.maxRemotePayload = msg.MaxPacketSize
 | 
			
		||||
	c.remoteWin.add(msg.PeersWindow)
 | 
			
		||||
	m.incomingChannels <- c
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *mux) OpenChannel(chanType string, extra []byte) (Channel, <-chan *Request, error) {
 | 
			
		||||
	ch, err := m.openChannel(chanType, extra)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ch, ch.incomingRequests, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *mux) openChannel(chanType string, extra []byte) (*channel, error) {
 | 
			
		||||
	ch := m.newChannel(chanType, channelOutbound, extra)
 | 
			
		||||
 | 
			
		||||
	ch.maxIncomingPayload = channelMaxPacket
 | 
			
		||||
 | 
			
		||||
	open := channelOpenMsg{
 | 
			
		||||
		ChanType:         chanType,
 | 
			
		||||
		PeersWindow:      ch.myWindow,
 | 
			
		||||
		MaxPacketSize:    ch.maxIncomingPayload,
 | 
			
		||||
		TypeSpecificData: extra,
 | 
			
		||||
		PeersID:          ch.localId,
 | 
			
		||||
	}
 | 
			
		||||
	if err := m.sendMessage(open); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch msg := (<-ch.msg).(type) {
 | 
			
		||||
	case *channelOpenConfirmMsg:
 | 
			
		||||
		return ch, nil
 | 
			
		||||
	case *channelOpenFailureMsg:
 | 
			
		||||
		return nil, &OpenChannelError{msg.Reason, msg.Message}
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("ssh: unexpected packet in response to channel open: %T", msg)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										582
									
								
								vendor/golang.org/x/crypto/ssh/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										582
									
								
								vendor/golang.org/x/crypto/ssh/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,582 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// The Permissions type holds fine-grained permissions that are
 | 
			
		||||
// specific to a user or a specific authentication method for a user.
 | 
			
		||||
// The Permissions value for a successful authentication attempt is
 | 
			
		||||
// available in ServerConn, so it can be used to pass information from
 | 
			
		||||
// the user-authentication phase to the application layer.
 | 
			
		||||
type Permissions struct {
 | 
			
		||||
	// CriticalOptions indicate restrictions to the default
 | 
			
		||||
	// permissions, and are typically used in conjunction with
 | 
			
		||||
	// user certificates. The standard for SSH certificates
 | 
			
		||||
	// defines "force-command" (only allow the given command to
 | 
			
		||||
	// execute) and "source-address" (only allow connections from
 | 
			
		||||
	// the given address). The SSH package currently only enforces
 | 
			
		||||
	// the "source-address" critical option. It is up to server
 | 
			
		||||
	// implementations to enforce other critical options, such as
 | 
			
		||||
	// "force-command", by checking them after the SSH handshake
 | 
			
		||||
	// is successful. In general, SSH servers should reject
 | 
			
		||||
	// connections that specify critical options that are unknown
 | 
			
		||||
	// or not supported.
 | 
			
		||||
	CriticalOptions map[string]string
 | 
			
		||||
 | 
			
		||||
	// Extensions are extra functionality that the server may
 | 
			
		||||
	// offer on authenticated connections. Lack of support for an
 | 
			
		||||
	// extension does not preclude authenticating a user. Common
 | 
			
		||||
	// extensions are "permit-agent-forwarding",
 | 
			
		||||
	// "permit-X11-forwarding". The Go SSH library currently does
 | 
			
		||||
	// not act on any extension, and it is up to server
 | 
			
		||||
	// implementations to honor them. Extensions can be used to
 | 
			
		||||
	// pass data from the authentication callbacks to the server
 | 
			
		||||
	// application layer.
 | 
			
		||||
	Extensions map[string]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServerConfig holds server specific configuration data.
 | 
			
		||||
type ServerConfig struct {
 | 
			
		||||
	// Config contains configuration shared between client and server.
 | 
			
		||||
	Config
 | 
			
		||||
 | 
			
		||||
	hostKeys []Signer
 | 
			
		||||
 | 
			
		||||
	// NoClientAuth is true if clients are allowed to connect without
 | 
			
		||||
	// authenticating.
 | 
			
		||||
	NoClientAuth bool
 | 
			
		||||
 | 
			
		||||
	// MaxAuthTries specifies the maximum number of authentication attempts
 | 
			
		||||
	// permitted per connection. If set to a negative number, the number of
 | 
			
		||||
	// attempts are unlimited. If set to zero, the number of attempts are limited
 | 
			
		||||
	// to 6.
 | 
			
		||||
	MaxAuthTries int
 | 
			
		||||
 | 
			
		||||
	// PasswordCallback, if non-nil, is called when a user
 | 
			
		||||
	// attempts to authenticate using a password.
 | 
			
		||||
	PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
 | 
			
		||||
 | 
			
		||||
	// PublicKeyCallback, if non-nil, is called when a client
 | 
			
		||||
	// offers a public key for authentication. It must return a nil error
 | 
			
		||||
	// if the given public key can be used to authenticate the
 | 
			
		||||
	// given user. For example, see CertChecker.Authenticate. A
 | 
			
		||||
	// call to this function does not guarantee that the key
 | 
			
		||||
	// offered is in fact used to authenticate. To record any data
 | 
			
		||||
	// depending on the public key, store it inside a
 | 
			
		||||
	// Permissions.Extensions entry.
 | 
			
		||||
	PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
 | 
			
		||||
 | 
			
		||||
	// KeyboardInteractiveCallback, if non-nil, is called when
 | 
			
		||||
	// keyboard-interactive authentication is selected (RFC
 | 
			
		||||
	// 4256). The client object's Challenge function should be
 | 
			
		||||
	// used to query the user. The callback may offer multiple
 | 
			
		||||
	// Challenge rounds. To avoid information leaks, the client
 | 
			
		||||
	// should be presented a challenge even if the user is
 | 
			
		||||
	// unknown.
 | 
			
		||||
	KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
 | 
			
		||||
 | 
			
		||||
	// AuthLogCallback, if non-nil, is called to log all authentication
 | 
			
		||||
	// attempts.
 | 
			
		||||
	AuthLogCallback func(conn ConnMetadata, method string, err error)
 | 
			
		||||
 | 
			
		||||
	// ServerVersion is the version identification string to announce in
 | 
			
		||||
	// the public handshake.
 | 
			
		||||
	// If empty, a reasonable default is used.
 | 
			
		||||
	// Note that RFC 4253 section 4.2 requires that this string start with
 | 
			
		||||
	// "SSH-2.0-".
 | 
			
		||||
	ServerVersion string
 | 
			
		||||
 | 
			
		||||
	// BannerCallback, if present, is called and the return string is sent to
 | 
			
		||||
	// the client after key exchange completed but before authentication.
 | 
			
		||||
	BannerCallback func(conn ConnMetadata) string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddHostKey adds a private key as a host key. If an existing host
 | 
			
		||||
// key exists with the same algorithm, it is overwritten. Each server
 | 
			
		||||
// config must have at least one host key.
 | 
			
		||||
func (s *ServerConfig) AddHostKey(key Signer) {
 | 
			
		||||
	for i, k := range s.hostKeys {
 | 
			
		||||
		if k.PublicKey().Type() == key.PublicKey().Type() {
 | 
			
		||||
			s.hostKeys[i] = key
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.hostKeys = append(s.hostKeys, key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// cachedPubKey contains the results of querying whether a public key is
 | 
			
		||||
// acceptable for a user.
 | 
			
		||||
type cachedPubKey struct {
 | 
			
		||||
	user       string
 | 
			
		||||
	pubKeyData []byte
 | 
			
		||||
	result     error
 | 
			
		||||
	perms      *Permissions
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const maxCachedPubKeys = 16
 | 
			
		||||
 | 
			
		||||
// pubKeyCache caches tests for public keys.  Since SSH clients
 | 
			
		||||
// will query whether a public key is acceptable before attempting to
 | 
			
		||||
// authenticate with it, we end up with duplicate queries for public
 | 
			
		||||
// key validity.  The cache only applies to a single ServerConn.
 | 
			
		||||
type pubKeyCache struct {
 | 
			
		||||
	keys []cachedPubKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// get returns the result for a given user/algo/key tuple.
 | 
			
		||||
func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) {
 | 
			
		||||
	for _, k := range c.keys {
 | 
			
		||||
		if k.user == user && bytes.Equal(k.pubKeyData, pubKeyData) {
 | 
			
		||||
			return k, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return cachedPubKey{}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// add adds the given tuple to the cache.
 | 
			
		||||
func (c *pubKeyCache) add(candidate cachedPubKey) {
 | 
			
		||||
	if len(c.keys) < maxCachedPubKeys {
 | 
			
		||||
		c.keys = append(c.keys, candidate)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServerConn is an authenticated SSH connection, as seen from the
 | 
			
		||||
// server
 | 
			
		||||
type ServerConn struct {
 | 
			
		||||
	Conn
 | 
			
		||||
 | 
			
		||||
	// If the succeeding authentication callback returned a
 | 
			
		||||
	// non-nil Permissions pointer, it is stored here.
 | 
			
		||||
	Permissions *Permissions
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewServerConn starts a new SSH server with c as the underlying
 | 
			
		||||
// transport.  It starts with a handshake and, if the handshake is
 | 
			
		||||
// unsuccessful, it closes the connection and returns an error.  The
 | 
			
		||||
// Request and NewChannel channels must be serviced, or the connection
 | 
			
		||||
// will hang.
 | 
			
		||||
func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
 | 
			
		||||
	fullConf := *config
 | 
			
		||||
	fullConf.SetDefaults()
 | 
			
		||||
	if fullConf.MaxAuthTries == 0 {
 | 
			
		||||
		fullConf.MaxAuthTries = 6
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s := &connection{
 | 
			
		||||
		sshConn: sshConn{conn: c},
 | 
			
		||||
	}
 | 
			
		||||
	perms, err := s.serverHandshake(&fullConf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.Close()
 | 
			
		||||
		return nil, nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &ServerConn{s, perms}, s.mux.incomingChannels, s.mux.incomingRequests, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// signAndMarshal signs the data with the appropriate algorithm,
 | 
			
		||||
// and serializes the result in SSH wire format.
 | 
			
		||||
func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) {
 | 
			
		||||
	sig, err := k.Sign(rand, data)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Marshal(sig), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handshake performs key exchange and user authentication.
 | 
			
		||||
func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) {
 | 
			
		||||
	if len(config.hostKeys) == 0 {
 | 
			
		||||
		return nil, errors.New("ssh: server has no host keys")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil && config.KeyboardInteractiveCallback == nil {
 | 
			
		||||
		return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.ServerVersion != "" {
 | 
			
		||||
		s.serverVersion = []byte(config.ServerVersion)
 | 
			
		||||
	} else {
 | 
			
		||||
		s.serverVersion = []byte(packageVersion)
 | 
			
		||||
	}
 | 
			
		||||
	var err error
 | 
			
		||||
	s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
 | 
			
		||||
	s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config)
 | 
			
		||||
 | 
			
		||||
	if err := s.transport.waitSession(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We just did the key change, so the session ID is established.
 | 
			
		||||
	s.sessionID = s.transport.getSessionID()
 | 
			
		||||
 | 
			
		||||
	var packet []byte
 | 
			
		||||
	if packet, err = s.transport.readPacket(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var serviceRequest serviceRequestMsg
 | 
			
		||||
	if err = Unmarshal(packet, &serviceRequest); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if serviceRequest.Service != serviceUserAuth {
 | 
			
		||||
		return nil, errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating")
 | 
			
		||||
	}
 | 
			
		||||
	serviceAccept := serviceAcceptMsg{
 | 
			
		||||
		Service: serviceUserAuth,
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	perms, err := s.serverAuthenticate(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	s.mux = newMux(s.transport)
 | 
			
		||||
	return perms, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isAcceptableAlgo(algo string) bool {
 | 
			
		||||
	switch algo {
 | 
			
		||||
	case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519,
 | 
			
		||||
		CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
 | 
			
		||||
	if addr == nil {
 | 
			
		||||
		return errors.New("ssh: no address known for client, but source-address match required")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tcpAddr, ok := addr.(*net.TCPAddr)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, sourceAddr := range strings.Split(sourceAddrs, ",") {
 | 
			
		||||
		if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil {
 | 
			
		||||
			if allowedIP.Equal(tcpAddr.IP) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			_, ipNet, err := net.ParseCIDR(sourceAddr)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if ipNet.Contains(tcpAddr.IP) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServerAuthError implements the error interface. It appends any authentication
 | 
			
		||||
// errors that may occur, and is returned if all of the authentication methods
 | 
			
		||||
// provided by the user failed to authenticate.
 | 
			
		||||
type ServerAuthError struct {
 | 
			
		||||
	// Errors contains authentication errors returned by the authentication
 | 
			
		||||
	// callback methods.
 | 
			
		||||
	Errors []error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l ServerAuthError) Error() string {
 | 
			
		||||
	var errs []string
 | 
			
		||||
	for _, err := range l.Errors {
 | 
			
		||||
		errs = append(errs, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return "[" + strings.Join(errs, ", ") + "]"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
 | 
			
		||||
	sessionID := s.transport.getSessionID()
 | 
			
		||||
	var cache pubKeyCache
 | 
			
		||||
	var perms *Permissions
 | 
			
		||||
 | 
			
		||||
	authFailures := 0
 | 
			
		||||
	var authErrs []error
 | 
			
		||||
	var displayedBanner bool
 | 
			
		||||
 | 
			
		||||
userAuthLoop:
 | 
			
		||||
	for {
 | 
			
		||||
		if authFailures >= config.MaxAuthTries && config.MaxAuthTries > 0 {
 | 
			
		||||
			discMsg := &disconnectMsg{
 | 
			
		||||
				Reason:  2,
 | 
			
		||||
				Message: "too many authentication failures",
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := s.transport.writePacket(Marshal(discMsg)); err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nil, discMsg
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var userAuthReq userAuthRequestMsg
 | 
			
		||||
		if packet, err := s.transport.readPacket(); err != nil {
 | 
			
		||||
			if err == io.EOF {
 | 
			
		||||
				return nil, &ServerAuthError{Errors: authErrs}
 | 
			
		||||
			}
 | 
			
		||||
			return nil, err
 | 
			
		||||
		} else if err = Unmarshal(packet, &userAuthReq); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if userAuthReq.Service != serviceSSH {
 | 
			
		||||
			return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.user = userAuthReq.User
 | 
			
		||||
 | 
			
		||||
		if !displayedBanner && config.BannerCallback != nil {
 | 
			
		||||
			displayedBanner = true
 | 
			
		||||
			msg := config.BannerCallback(s)
 | 
			
		||||
			if msg != "" {
 | 
			
		||||
				bannerMsg := &userAuthBannerMsg{
 | 
			
		||||
					Message: msg,
 | 
			
		||||
				}
 | 
			
		||||
				if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
 | 
			
		||||
					return nil, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		perms = nil
 | 
			
		||||
		authErr := errors.New("no auth passed yet")
 | 
			
		||||
 | 
			
		||||
		switch userAuthReq.Method {
 | 
			
		||||
		case "none":
 | 
			
		||||
			if config.NoClientAuth {
 | 
			
		||||
				authErr = nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// allow initial attempt of 'none' without penalty
 | 
			
		||||
			if authFailures == 0 {
 | 
			
		||||
				authFailures--
 | 
			
		||||
			}
 | 
			
		||||
		case "password":
 | 
			
		||||
			if config.PasswordCallback == nil {
 | 
			
		||||
				authErr = errors.New("ssh: password auth not configured")
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			payload := userAuthReq.Payload
 | 
			
		||||
			if len(payload) < 1 || payload[0] != 0 {
 | 
			
		||||
				return nil, parseError(msgUserAuthRequest)
 | 
			
		||||
			}
 | 
			
		||||
			payload = payload[1:]
 | 
			
		||||
			password, payload, ok := parseString(payload)
 | 
			
		||||
			if !ok || len(payload) > 0 {
 | 
			
		||||
				return nil, parseError(msgUserAuthRequest)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			perms, authErr = config.PasswordCallback(s, password)
 | 
			
		||||
		case "keyboard-interactive":
 | 
			
		||||
			if config.KeyboardInteractiveCallback == nil {
 | 
			
		||||
				authErr = errors.New("ssh: keyboard-interactive auth not configubred")
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			prompter := &sshClientKeyboardInteractive{s}
 | 
			
		||||
			perms, authErr = config.KeyboardInteractiveCallback(s, prompter.Challenge)
 | 
			
		||||
		case "publickey":
 | 
			
		||||
			if config.PublicKeyCallback == nil {
 | 
			
		||||
				authErr = errors.New("ssh: publickey auth not configured")
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			payload := userAuthReq.Payload
 | 
			
		||||
			if len(payload) < 1 {
 | 
			
		||||
				return nil, parseError(msgUserAuthRequest)
 | 
			
		||||
			}
 | 
			
		||||
			isQuery := payload[0] == 0
 | 
			
		||||
			payload = payload[1:]
 | 
			
		||||
			algoBytes, payload, ok := parseString(payload)
 | 
			
		||||
			if !ok {
 | 
			
		||||
				return nil, parseError(msgUserAuthRequest)
 | 
			
		||||
			}
 | 
			
		||||
			algo := string(algoBytes)
 | 
			
		||||
			if !isAcceptableAlgo(algo) {
 | 
			
		||||
				authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			pubKeyData, payload, ok := parseString(payload)
 | 
			
		||||
			if !ok {
 | 
			
		||||
				return nil, parseError(msgUserAuthRequest)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			pubKey, err := ParsePublicKey(pubKeyData)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			candidate, ok := cache.get(s.user, pubKeyData)
 | 
			
		||||
			if !ok {
 | 
			
		||||
				candidate.user = s.user
 | 
			
		||||
				candidate.pubKeyData = pubKeyData
 | 
			
		||||
				candidate.perms, candidate.result = config.PublicKeyCallback(s, pubKey)
 | 
			
		||||
				if candidate.result == nil && candidate.perms != nil && candidate.perms.CriticalOptions != nil && candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" {
 | 
			
		||||
					candidate.result = checkSourceAddress(
 | 
			
		||||
						s.RemoteAddr(),
 | 
			
		||||
						candidate.perms.CriticalOptions[sourceAddressCriticalOption])
 | 
			
		||||
				}
 | 
			
		||||
				cache.add(candidate)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if isQuery {
 | 
			
		||||
				// The client can query if the given public key
 | 
			
		||||
				// would be okay.
 | 
			
		||||
 | 
			
		||||
				if len(payload) > 0 {
 | 
			
		||||
					return nil, parseError(msgUserAuthRequest)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if candidate.result == nil {
 | 
			
		||||
					okMsg := userAuthPubKeyOkMsg{
 | 
			
		||||
						Algo:   algo,
 | 
			
		||||
						PubKey: pubKeyData,
 | 
			
		||||
					}
 | 
			
		||||
					if err = s.transport.writePacket(Marshal(&okMsg)); err != nil {
 | 
			
		||||
						return nil, err
 | 
			
		||||
					}
 | 
			
		||||
					continue userAuthLoop
 | 
			
		||||
				}
 | 
			
		||||
				authErr = candidate.result
 | 
			
		||||
			} else {
 | 
			
		||||
				sig, payload, ok := parseSignature(payload)
 | 
			
		||||
				if !ok || len(payload) > 0 {
 | 
			
		||||
					return nil, parseError(msgUserAuthRequest)
 | 
			
		||||
				}
 | 
			
		||||
				// Ensure the public key algo and signature algo
 | 
			
		||||
				// are supported.  Compare the private key
 | 
			
		||||
				// algorithm name that corresponds to algo with
 | 
			
		||||
				// sig.Format.  This is usually the same, but
 | 
			
		||||
				// for certs, the names differ.
 | 
			
		||||
				if !isAcceptableAlgo(sig.Format) {
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				signedData := buildDataSignedForAuth(sessionID, userAuthReq, algoBytes, pubKeyData)
 | 
			
		||||
 | 
			
		||||
				if err := pubKey.Verify(signedData, sig); err != nil {
 | 
			
		||||
					return nil, err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				authErr = candidate.result
 | 
			
		||||
				perms = candidate.perms
 | 
			
		||||
			}
 | 
			
		||||
		default:
 | 
			
		||||
			authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		authErrs = append(authErrs, authErr)
 | 
			
		||||
 | 
			
		||||
		if config.AuthLogCallback != nil {
 | 
			
		||||
			config.AuthLogCallback(s, userAuthReq.Method, authErr)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if authErr == nil {
 | 
			
		||||
			break userAuthLoop
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		authFailures++
 | 
			
		||||
 | 
			
		||||
		var failureMsg userAuthFailureMsg
 | 
			
		||||
		if config.PasswordCallback != nil {
 | 
			
		||||
			failureMsg.Methods = append(failureMsg.Methods, "password")
 | 
			
		||||
		}
 | 
			
		||||
		if config.PublicKeyCallback != nil {
 | 
			
		||||
			failureMsg.Methods = append(failureMsg.Methods, "publickey")
 | 
			
		||||
		}
 | 
			
		||||
		if config.KeyboardInteractiveCallback != nil {
 | 
			
		||||
			failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(failureMsg.Methods) == 0 {
 | 
			
		||||
			return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return perms, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sshClientKeyboardInteractive implements a ClientKeyboardInteractive by
 | 
			
		||||
// asking the client on the other side of a ServerConn.
 | 
			
		||||
type sshClientKeyboardInteractive struct {
 | 
			
		||||
	*connection
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
 | 
			
		||||
	if len(questions) != len(echos) {
 | 
			
		||||
		return nil, errors.New("ssh: echos and questions must have equal length")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var prompts []byte
 | 
			
		||||
	for i := range questions {
 | 
			
		||||
		prompts = appendString(prompts, questions[i])
 | 
			
		||||
		prompts = appendBool(prompts, echos[i])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{
 | 
			
		||||
		Instruction: instruction,
 | 
			
		||||
		NumPrompts:  uint32(len(questions)),
 | 
			
		||||
		Prompts:     prompts,
 | 
			
		||||
	})); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packet, err := c.transport.readPacket()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if packet[0] != msgUserAuthInfoResponse {
 | 
			
		||||
		return nil, unexpectedMessageError(msgUserAuthInfoResponse, packet[0])
 | 
			
		||||
	}
 | 
			
		||||
	packet = packet[1:]
 | 
			
		||||
 | 
			
		||||
	n, packet, ok := parseUint32(packet)
 | 
			
		||||
	if !ok || int(n) != len(questions) {
 | 
			
		||||
		return nil, parseError(msgUserAuthInfoResponse)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := uint32(0); i < n; i++ {
 | 
			
		||||
		ans, rest, ok := parseString(packet)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return nil, parseError(msgUserAuthInfoResponse)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		answers = append(answers, string(ans))
 | 
			
		||||
		packet = rest
 | 
			
		||||
	}
 | 
			
		||||
	if len(packet) != 0 {
 | 
			
		||||
		return nil, errors.New("ssh: junk at end of message")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return answers, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										647
									
								
								vendor/golang.org/x/crypto/ssh/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										647
									
								
								vendor/golang.org/x/crypto/ssh/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,647 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
// Session implements an interactive session described in
 | 
			
		||||
// "RFC 4254, section 6".
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Signal string
 | 
			
		||||
 | 
			
		||||
// POSIX signals as listed in RFC 4254 Section 6.10.
 | 
			
		||||
const (
 | 
			
		||||
	SIGABRT Signal = "ABRT"
 | 
			
		||||
	SIGALRM Signal = "ALRM"
 | 
			
		||||
	SIGFPE  Signal = "FPE"
 | 
			
		||||
	SIGHUP  Signal = "HUP"
 | 
			
		||||
	SIGILL  Signal = "ILL"
 | 
			
		||||
	SIGINT  Signal = "INT"
 | 
			
		||||
	SIGKILL Signal = "KILL"
 | 
			
		||||
	SIGPIPE Signal = "PIPE"
 | 
			
		||||
	SIGQUIT Signal = "QUIT"
 | 
			
		||||
	SIGSEGV Signal = "SEGV"
 | 
			
		||||
	SIGTERM Signal = "TERM"
 | 
			
		||||
	SIGUSR1 Signal = "USR1"
 | 
			
		||||
	SIGUSR2 Signal = "USR2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var signals = map[Signal]int{
 | 
			
		||||
	SIGABRT: 6,
 | 
			
		||||
	SIGALRM: 14,
 | 
			
		||||
	SIGFPE:  8,
 | 
			
		||||
	SIGHUP:  1,
 | 
			
		||||
	SIGILL:  4,
 | 
			
		||||
	SIGINT:  2,
 | 
			
		||||
	SIGKILL: 9,
 | 
			
		||||
	SIGPIPE: 13,
 | 
			
		||||
	SIGQUIT: 3,
 | 
			
		||||
	SIGSEGV: 11,
 | 
			
		||||
	SIGTERM: 15,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TerminalModes map[uint8]uint32
 | 
			
		||||
 | 
			
		||||
// POSIX terminal mode flags as listed in RFC 4254 Section 8.
 | 
			
		||||
const (
 | 
			
		||||
	tty_OP_END    = 0
 | 
			
		||||
	VINTR         = 1
 | 
			
		||||
	VQUIT         = 2
 | 
			
		||||
	VERASE        = 3
 | 
			
		||||
	VKILL         = 4
 | 
			
		||||
	VEOF          = 5
 | 
			
		||||
	VEOL          = 6
 | 
			
		||||
	VEOL2         = 7
 | 
			
		||||
	VSTART        = 8
 | 
			
		||||
	VSTOP         = 9
 | 
			
		||||
	VSUSP         = 10
 | 
			
		||||
	VDSUSP        = 11
 | 
			
		||||
	VREPRINT      = 12
 | 
			
		||||
	VWERASE       = 13
 | 
			
		||||
	VLNEXT        = 14
 | 
			
		||||
	VFLUSH        = 15
 | 
			
		||||
	VSWTCH        = 16
 | 
			
		||||
	VSTATUS       = 17
 | 
			
		||||
	VDISCARD      = 18
 | 
			
		||||
	IGNPAR        = 30
 | 
			
		||||
	PARMRK        = 31
 | 
			
		||||
	INPCK         = 32
 | 
			
		||||
	ISTRIP        = 33
 | 
			
		||||
	INLCR         = 34
 | 
			
		||||
	IGNCR         = 35
 | 
			
		||||
	ICRNL         = 36
 | 
			
		||||
	IUCLC         = 37
 | 
			
		||||
	IXON          = 38
 | 
			
		||||
	IXANY         = 39
 | 
			
		||||
	IXOFF         = 40
 | 
			
		||||
	IMAXBEL       = 41
 | 
			
		||||
	ISIG          = 50
 | 
			
		||||
	ICANON        = 51
 | 
			
		||||
	XCASE         = 52
 | 
			
		||||
	ECHO          = 53
 | 
			
		||||
	ECHOE         = 54
 | 
			
		||||
	ECHOK         = 55
 | 
			
		||||
	ECHONL        = 56
 | 
			
		||||
	NOFLSH        = 57
 | 
			
		||||
	TOSTOP        = 58
 | 
			
		||||
	IEXTEN        = 59
 | 
			
		||||
	ECHOCTL       = 60
 | 
			
		||||
	ECHOKE        = 61
 | 
			
		||||
	PENDIN        = 62
 | 
			
		||||
	OPOST         = 70
 | 
			
		||||
	OLCUC         = 71
 | 
			
		||||
	ONLCR         = 72
 | 
			
		||||
	OCRNL         = 73
 | 
			
		||||
	ONOCR         = 74
 | 
			
		||||
	ONLRET        = 75
 | 
			
		||||
	CS7           = 90
 | 
			
		||||
	CS8           = 91
 | 
			
		||||
	PARENB        = 92
 | 
			
		||||
	PARODD        = 93
 | 
			
		||||
	TTY_OP_ISPEED = 128
 | 
			
		||||
	TTY_OP_OSPEED = 129
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A Session represents a connection to a remote command or shell.
 | 
			
		||||
type Session struct {
 | 
			
		||||
	// Stdin specifies the remote process's standard input.
 | 
			
		||||
	// If Stdin is nil, the remote process reads from an empty
 | 
			
		||||
	// bytes.Buffer.
 | 
			
		||||
	Stdin io.Reader
 | 
			
		||||
 | 
			
		||||
	// Stdout and Stderr specify the remote process's standard
 | 
			
		||||
	// output and error.
 | 
			
		||||
	//
 | 
			
		||||
	// If either is nil, Run connects the corresponding file
 | 
			
		||||
	// descriptor to an instance of ioutil.Discard. There is a
 | 
			
		||||
	// fixed amount of buffering that is shared for the two streams.
 | 
			
		||||
	// If either blocks it may eventually cause the remote
 | 
			
		||||
	// command to block.
 | 
			
		||||
	Stdout io.Writer
 | 
			
		||||
	Stderr io.Writer
 | 
			
		||||
 | 
			
		||||
	ch        Channel // the channel backing this session
 | 
			
		||||
	started   bool    // true once Start, Run or Shell is invoked.
 | 
			
		||||
	copyFuncs []func() error
 | 
			
		||||
	errors    chan error // one send per copyFunc
 | 
			
		||||
 | 
			
		||||
	// true if pipe method is active
 | 
			
		||||
	stdinpipe, stdoutpipe, stderrpipe bool
 | 
			
		||||
 | 
			
		||||
	// stdinPipeWriter is non-nil if StdinPipe has not been called
 | 
			
		||||
	// and Stdin was specified by the user; it is the write end of
 | 
			
		||||
	// a pipe connecting Session.Stdin to the stdin channel.
 | 
			
		||||
	stdinPipeWriter io.WriteCloser
 | 
			
		||||
 | 
			
		||||
	exitStatus chan error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendRequest sends an out-of-band channel request on the SSH channel
 | 
			
		||||
// underlying the session.
 | 
			
		||||
func (s *Session) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
 | 
			
		||||
	return s.ch.SendRequest(name, wantReply, payload)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) Close() error {
 | 
			
		||||
	return s.ch.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 4254 Section 6.4.
 | 
			
		||||
type setenvRequest struct {
 | 
			
		||||
	Name  string
 | 
			
		||||
	Value string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Setenv sets an environment variable that will be applied to any
 | 
			
		||||
// command executed by Shell or Run.
 | 
			
		||||
func (s *Session) Setenv(name, value string) error {
 | 
			
		||||
	msg := setenvRequest{
 | 
			
		||||
		Name:  name,
 | 
			
		||||
		Value: value,
 | 
			
		||||
	}
 | 
			
		||||
	ok, err := s.ch.SendRequest("env", true, Marshal(&msg))
 | 
			
		||||
	if err == nil && !ok {
 | 
			
		||||
		err = errors.New("ssh: setenv failed")
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 4254 Section 6.2.
 | 
			
		||||
type ptyRequestMsg struct {
 | 
			
		||||
	Term     string
 | 
			
		||||
	Columns  uint32
 | 
			
		||||
	Rows     uint32
 | 
			
		||||
	Width    uint32
 | 
			
		||||
	Height   uint32
 | 
			
		||||
	Modelist string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequestPty requests the association of a pty with the session on the remote host.
 | 
			
		||||
func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) error {
 | 
			
		||||
	var tm []byte
 | 
			
		||||
	for k, v := range termmodes {
 | 
			
		||||
		kv := struct {
 | 
			
		||||
			Key byte
 | 
			
		||||
			Val uint32
 | 
			
		||||
		}{k, v}
 | 
			
		||||
 | 
			
		||||
		tm = append(tm, Marshal(&kv)...)
 | 
			
		||||
	}
 | 
			
		||||
	tm = append(tm, tty_OP_END)
 | 
			
		||||
	req := ptyRequestMsg{
 | 
			
		||||
		Term:     term,
 | 
			
		||||
		Columns:  uint32(w),
 | 
			
		||||
		Rows:     uint32(h),
 | 
			
		||||
		Width:    uint32(w * 8),
 | 
			
		||||
		Height:   uint32(h * 8),
 | 
			
		||||
		Modelist: string(tm),
 | 
			
		||||
	}
 | 
			
		||||
	ok, err := s.ch.SendRequest("pty-req", true, Marshal(&req))
 | 
			
		||||
	if err == nil && !ok {
 | 
			
		||||
		err = errors.New("ssh: pty-req failed")
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 4254 Section 6.5.
 | 
			
		||||
type subsystemRequestMsg struct {
 | 
			
		||||
	Subsystem string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequestSubsystem requests the association of a subsystem with the session on the remote host.
 | 
			
		||||
// A subsystem is a predefined command that runs in the background when the ssh session is initiated
 | 
			
		||||
func (s *Session) RequestSubsystem(subsystem string) error {
 | 
			
		||||
	msg := subsystemRequestMsg{
 | 
			
		||||
		Subsystem: subsystem,
 | 
			
		||||
	}
 | 
			
		||||
	ok, err := s.ch.SendRequest("subsystem", true, Marshal(&msg))
 | 
			
		||||
	if err == nil && !ok {
 | 
			
		||||
		err = errors.New("ssh: subsystem request failed")
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 4254 Section 6.7.
 | 
			
		||||
type ptyWindowChangeMsg struct {
 | 
			
		||||
	Columns uint32
 | 
			
		||||
	Rows    uint32
 | 
			
		||||
	Width   uint32
 | 
			
		||||
	Height  uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WindowChange informs the remote host about a terminal window dimension change to h rows and w columns.
 | 
			
		||||
func (s *Session) WindowChange(h, w int) error {
 | 
			
		||||
	req := ptyWindowChangeMsg{
 | 
			
		||||
		Columns: uint32(w),
 | 
			
		||||
		Rows:    uint32(h),
 | 
			
		||||
		Width:   uint32(w * 8),
 | 
			
		||||
		Height:  uint32(h * 8),
 | 
			
		||||
	}
 | 
			
		||||
	_, err := s.ch.SendRequest("window-change", false, Marshal(&req))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 4254 Section 6.9.
 | 
			
		||||
type signalMsg struct {
 | 
			
		||||
	Signal string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Signal sends the given signal to the remote process.
 | 
			
		||||
// sig is one of the SIG* constants.
 | 
			
		||||
func (s *Session) Signal(sig Signal) error {
 | 
			
		||||
	msg := signalMsg{
 | 
			
		||||
		Signal: string(sig),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := s.ch.SendRequest("signal", false, Marshal(&msg))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 4254 Section 6.5.
 | 
			
		||||
type execMsg struct {
 | 
			
		||||
	Command string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start runs cmd on the remote host. Typically, the remote
 | 
			
		||||
// server passes cmd to the shell for interpretation.
 | 
			
		||||
// A Session only accepts one call to Run, Start or Shell.
 | 
			
		||||
func (s *Session) Start(cmd string) error {
 | 
			
		||||
	if s.started {
 | 
			
		||||
		return errors.New("ssh: session already started")
 | 
			
		||||
	}
 | 
			
		||||
	req := execMsg{
 | 
			
		||||
		Command: cmd,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ok, err := s.ch.SendRequest("exec", true, Marshal(&req))
 | 
			
		||||
	if err == nil && !ok {
 | 
			
		||||
		err = fmt.Errorf("ssh: command %v failed", cmd)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return s.start()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Run runs cmd on the remote host. Typically, the remote
 | 
			
		||||
// server passes cmd to the shell for interpretation.
 | 
			
		||||
// A Session only accepts one call to Run, Start, Shell, Output,
 | 
			
		||||
// or CombinedOutput.
 | 
			
		||||
//
 | 
			
		||||
// The returned error is nil if the command runs, has no problems
 | 
			
		||||
// copying stdin, stdout, and stderr, and exits with a zero exit
 | 
			
		||||
// status.
 | 
			
		||||
//
 | 
			
		||||
// If the remote server does not send an exit status, an error of type
 | 
			
		||||
// *ExitMissingError is returned. If the command completes
 | 
			
		||||
// unsuccessfully or is interrupted by a signal, the error is of type
 | 
			
		||||
// *ExitError. Other error types may be returned for I/O problems.
 | 
			
		||||
func (s *Session) Run(cmd string) error {
 | 
			
		||||
	err := s.Start(cmd)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return s.Wait()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Output runs cmd on the remote host and returns its standard output.
 | 
			
		||||
func (s *Session) Output(cmd string) ([]byte, error) {
 | 
			
		||||
	if s.Stdout != nil {
 | 
			
		||||
		return nil, errors.New("ssh: Stdout already set")
 | 
			
		||||
	}
 | 
			
		||||
	var b bytes.Buffer
 | 
			
		||||
	s.Stdout = &b
 | 
			
		||||
	err := s.Run(cmd)
 | 
			
		||||
	return b.Bytes(), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type singleWriter struct {
 | 
			
		||||
	b  bytes.Buffer
 | 
			
		||||
	mu sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *singleWriter) Write(p []byte) (int, error) {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	return w.b.Write(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CombinedOutput runs cmd on the remote host and returns its combined
 | 
			
		||||
// standard output and standard error.
 | 
			
		||||
func (s *Session) CombinedOutput(cmd string) ([]byte, error) {
 | 
			
		||||
	if s.Stdout != nil {
 | 
			
		||||
		return nil, errors.New("ssh: Stdout already set")
 | 
			
		||||
	}
 | 
			
		||||
	if s.Stderr != nil {
 | 
			
		||||
		return nil, errors.New("ssh: Stderr already set")
 | 
			
		||||
	}
 | 
			
		||||
	var b singleWriter
 | 
			
		||||
	s.Stdout = &b
 | 
			
		||||
	s.Stderr = &b
 | 
			
		||||
	err := s.Run(cmd)
 | 
			
		||||
	return b.b.Bytes(), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Shell starts a login shell on the remote host. A Session only
 | 
			
		||||
// accepts one call to Run, Start, Shell, Output, or CombinedOutput.
 | 
			
		||||
func (s *Session) Shell() error {
 | 
			
		||||
	if s.started {
 | 
			
		||||
		return errors.New("ssh: session already started")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ok, err := s.ch.SendRequest("shell", true, nil)
 | 
			
		||||
	if err == nil && !ok {
 | 
			
		||||
		return errors.New("ssh: could not start shell")
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return s.start()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) start() error {
 | 
			
		||||
	s.started = true
 | 
			
		||||
 | 
			
		||||
	type F func(*Session)
 | 
			
		||||
	for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} {
 | 
			
		||||
		setupFd(s)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.errors = make(chan error, len(s.copyFuncs))
 | 
			
		||||
	for _, fn := range s.copyFuncs {
 | 
			
		||||
		go func(fn func() error) {
 | 
			
		||||
			s.errors <- fn()
 | 
			
		||||
		}(fn)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Wait waits for the remote command to exit.
 | 
			
		||||
//
 | 
			
		||||
// The returned error is nil if the command runs, has no problems
 | 
			
		||||
// copying stdin, stdout, and stderr, and exits with a zero exit
 | 
			
		||||
// status.
 | 
			
		||||
//
 | 
			
		||||
// If the remote server does not send an exit status, an error of type
 | 
			
		||||
// *ExitMissingError is returned. If the command completes
 | 
			
		||||
// unsuccessfully or is interrupted by a signal, the error is of type
 | 
			
		||||
// *ExitError. Other error types may be returned for I/O problems.
 | 
			
		||||
func (s *Session) Wait() error {
 | 
			
		||||
	if !s.started {
 | 
			
		||||
		return errors.New("ssh: session not started")
 | 
			
		||||
	}
 | 
			
		||||
	waitErr := <-s.exitStatus
 | 
			
		||||
 | 
			
		||||
	if s.stdinPipeWriter != nil {
 | 
			
		||||
		s.stdinPipeWriter.Close()
 | 
			
		||||
	}
 | 
			
		||||
	var copyError error
 | 
			
		||||
	for range s.copyFuncs {
 | 
			
		||||
		if err := <-s.errors; err != nil && copyError == nil {
 | 
			
		||||
			copyError = err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if waitErr != nil {
 | 
			
		||||
		return waitErr
 | 
			
		||||
	}
 | 
			
		||||
	return copyError
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) wait(reqs <-chan *Request) error {
 | 
			
		||||
	wm := Waitmsg{status: -1}
 | 
			
		||||
	// Wait for msg channel to be closed before returning.
 | 
			
		||||
	for msg := range reqs {
 | 
			
		||||
		switch msg.Type {
 | 
			
		||||
		case "exit-status":
 | 
			
		||||
			wm.status = int(binary.BigEndian.Uint32(msg.Payload))
 | 
			
		||||
		case "exit-signal":
 | 
			
		||||
			var sigval struct {
 | 
			
		||||
				Signal     string
 | 
			
		||||
				CoreDumped bool
 | 
			
		||||
				Error      string
 | 
			
		||||
				Lang       string
 | 
			
		||||
			}
 | 
			
		||||
			if err := Unmarshal(msg.Payload, &sigval); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Must sanitize strings?
 | 
			
		||||
			wm.signal = sigval.Signal
 | 
			
		||||
			wm.msg = sigval.Error
 | 
			
		||||
			wm.lang = sigval.Lang
 | 
			
		||||
		default:
 | 
			
		||||
			// This handles keepalives and matches
 | 
			
		||||
			// OpenSSH's behaviour.
 | 
			
		||||
			if msg.WantReply {
 | 
			
		||||
				msg.Reply(false, nil)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if wm.status == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if wm.status == -1 {
 | 
			
		||||
		// exit-status was never sent from server
 | 
			
		||||
		if wm.signal == "" {
 | 
			
		||||
			// signal was not sent either.  RFC 4254
 | 
			
		||||
			// section 6.10 recommends against this
 | 
			
		||||
			// behavior, but it is allowed, so we let
 | 
			
		||||
			// clients handle it.
 | 
			
		||||
			return &ExitMissingError{}
 | 
			
		||||
		}
 | 
			
		||||
		wm.status = 128
 | 
			
		||||
		if _, ok := signals[Signal(wm.signal)]; ok {
 | 
			
		||||
			wm.status += signals[Signal(wm.signal)]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &ExitError{wm}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExitMissingError is returned if a session is torn down cleanly, but
 | 
			
		||||
// the server sends no confirmation of the exit status.
 | 
			
		||||
type ExitMissingError struct{}
 | 
			
		||||
 | 
			
		||||
func (e *ExitMissingError) Error() string {
 | 
			
		||||
	return "wait: remote command exited without exit status or exit signal"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) stdin() {
 | 
			
		||||
	if s.stdinpipe {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var stdin io.Reader
 | 
			
		||||
	if s.Stdin == nil {
 | 
			
		||||
		stdin = new(bytes.Buffer)
 | 
			
		||||
	} else {
 | 
			
		||||
		r, w := io.Pipe()
 | 
			
		||||
		go func() {
 | 
			
		||||
			_, err := io.Copy(w, s.Stdin)
 | 
			
		||||
			w.CloseWithError(err)
 | 
			
		||||
		}()
 | 
			
		||||
		stdin, s.stdinPipeWriter = r, w
 | 
			
		||||
	}
 | 
			
		||||
	s.copyFuncs = append(s.copyFuncs, func() error {
 | 
			
		||||
		_, err := io.Copy(s.ch, stdin)
 | 
			
		||||
		if err1 := s.ch.CloseWrite(); err == nil && err1 != io.EOF {
 | 
			
		||||
			err = err1
 | 
			
		||||
		}
 | 
			
		||||
		return err
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) stdout() {
 | 
			
		||||
	if s.stdoutpipe {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if s.Stdout == nil {
 | 
			
		||||
		s.Stdout = ioutil.Discard
 | 
			
		||||
	}
 | 
			
		||||
	s.copyFuncs = append(s.copyFuncs, func() error {
 | 
			
		||||
		_, err := io.Copy(s.Stdout, s.ch)
 | 
			
		||||
		return err
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) stderr() {
 | 
			
		||||
	if s.stderrpipe {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if s.Stderr == nil {
 | 
			
		||||
		s.Stderr = ioutil.Discard
 | 
			
		||||
	}
 | 
			
		||||
	s.copyFuncs = append(s.copyFuncs, func() error {
 | 
			
		||||
		_, err := io.Copy(s.Stderr, s.ch.Stderr())
 | 
			
		||||
		return err
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sessionStdin reroutes Close to CloseWrite.
 | 
			
		||||
type sessionStdin struct {
 | 
			
		||||
	io.Writer
 | 
			
		||||
	ch Channel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *sessionStdin) Close() error {
 | 
			
		||||
	return s.ch.CloseWrite()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StdinPipe returns a pipe that will be connected to the
 | 
			
		||||
// remote command's standard input when the command starts.
 | 
			
		||||
func (s *Session) StdinPipe() (io.WriteCloser, error) {
 | 
			
		||||
	if s.Stdin != nil {
 | 
			
		||||
		return nil, errors.New("ssh: Stdin already set")
 | 
			
		||||
	}
 | 
			
		||||
	if s.started {
 | 
			
		||||
		return nil, errors.New("ssh: StdinPipe after process started")
 | 
			
		||||
	}
 | 
			
		||||
	s.stdinpipe = true
 | 
			
		||||
	return &sessionStdin{s.ch, s.ch}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StdoutPipe returns a pipe that will be connected to the
 | 
			
		||||
// remote command's standard output when the command starts.
 | 
			
		||||
// There is a fixed amount of buffering that is shared between
 | 
			
		||||
// stdout and stderr streams. If the StdoutPipe reader is
 | 
			
		||||
// not serviced fast enough it may eventually cause the
 | 
			
		||||
// remote command to block.
 | 
			
		||||
func (s *Session) StdoutPipe() (io.Reader, error) {
 | 
			
		||||
	if s.Stdout != nil {
 | 
			
		||||
		return nil, errors.New("ssh: Stdout already set")
 | 
			
		||||
	}
 | 
			
		||||
	if s.started {
 | 
			
		||||
		return nil, errors.New("ssh: StdoutPipe after process started")
 | 
			
		||||
	}
 | 
			
		||||
	s.stdoutpipe = true
 | 
			
		||||
	return s.ch, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StderrPipe returns a pipe that will be connected to the
 | 
			
		||||
// remote command's standard error when the command starts.
 | 
			
		||||
// There is a fixed amount of buffering that is shared between
 | 
			
		||||
// stdout and stderr streams. If the StderrPipe reader is
 | 
			
		||||
// not serviced fast enough it may eventually cause the
 | 
			
		||||
// remote command to block.
 | 
			
		||||
func (s *Session) StderrPipe() (io.Reader, error) {
 | 
			
		||||
	if s.Stderr != nil {
 | 
			
		||||
		return nil, errors.New("ssh: Stderr already set")
 | 
			
		||||
	}
 | 
			
		||||
	if s.started {
 | 
			
		||||
		return nil, errors.New("ssh: StderrPipe after process started")
 | 
			
		||||
	}
 | 
			
		||||
	s.stderrpipe = true
 | 
			
		||||
	return s.ch.Stderr(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newSession returns a new interactive session on the remote host.
 | 
			
		||||
func newSession(ch Channel, reqs <-chan *Request) (*Session, error) {
 | 
			
		||||
	s := &Session{
 | 
			
		||||
		ch: ch,
 | 
			
		||||
	}
 | 
			
		||||
	s.exitStatus = make(chan error, 1)
 | 
			
		||||
	go func() {
 | 
			
		||||
		s.exitStatus <- s.wait(reqs)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return s, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An ExitError reports unsuccessful completion of a remote command.
 | 
			
		||||
type ExitError struct {
 | 
			
		||||
	Waitmsg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *ExitError) Error() string {
 | 
			
		||||
	return e.Waitmsg.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Waitmsg stores the information about an exited remote command
 | 
			
		||||
// as reported by Wait.
 | 
			
		||||
type Waitmsg struct {
 | 
			
		||||
	status int
 | 
			
		||||
	signal string
 | 
			
		||||
	msg    string
 | 
			
		||||
	lang   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExitStatus returns the exit status of the remote command.
 | 
			
		||||
func (w Waitmsg) ExitStatus() int {
 | 
			
		||||
	return w.status
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Signal returns the exit signal of the remote command if
 | 
			
		||||
// it was terminated violently.
 | 
			
		||||
func (w Waitmsg) Signal() string {
 | 
			
		||||
	return w.signal
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Msg returns the exit message given by the remote command
 | 
			
		||||
func (w Waitmsg) Msg() string {
 | 
			
		||||
	return w.msg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Lang returns the language tag. See RFC 3066
 | 
			
		||||
func (w Waitmsg) Lang() string {
 | 
			
		||||
	return w.lang
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w Waitmsg) String() string {
 | 
			
		||||
	str := fmt.Sprintf("Process exited with status %v", w.status)
 | 
			
		||||
	if w.signal != "" {
 | 
			
		||||
		str += fmt.Sprintf(" from signal %v", w.signal)
 | 
			
		||||
	}
 | 
			
		||||
	if w.msg != "" {
 | 
			
		||||
		str += fmt.Sprintf(". Reason was: %v", w.msg)
 | 
			
		||||
	}
 | 
			
		||||
	return str
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										115
									
								
								vendor/golang.org/x/crypto/ssh/streamlocal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								vendor/golang.org/x/crypto/ssh/streamlocal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// streamLocalChannelOpenDirectMsg is a struct used for SSH_MSG_CHANNEL_OPEN message
 | 
			
		||||
// with "direct-streamlocal@openssh.com" string.
 | 
			
		||||
//
 | 
			
		||||
// See openssh-portable/PROTOCOL, section 2.4. connection: Unix domain socket forwarding
 | 
			
		||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL#L235
 | 
			
		||||
type streamLocalChannelOpenDirectMsg struct {
 | 
			
		||||
	socketPath string
 | 
			
		||||
	reserved0  string
 | 
			
		||||
	reserved1  uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// forwardedStreamLocalPayload is a struct used for SSH_MSG_CHANNEL_OPEN message
 | 
			
		||||
// with "forwarded-streamlocal@openssh.com" string.
 | 
			
		||||
type forwardedStreamLocalPayload struct {
 | 
			
		||||
	SocketPath string
 | 
			
		||||
	Reserved0  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// streamLocalChannelForwardMsg is a struct used for SSH2_MSG_GLOBAL_REQUEST message
 | 
			
		||||
// with "streamlocal-forward@openssh.com"/"cancel-streamlocal-forward@openssh.com" string.
 | 
			
		||||
type streamLocalChannelForwardMsg struct {
 | 
			
		||||
	socketPath string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListenUnix is similar to ListenTCP but uses a Unix domain socket.
 | 
			
		||||
func (c *Client) ListenUnix(socketPath string) (net.Listener, error) {
 | 
			
		||||
	m := streamLocalChannelForwardMsg{
 | 
			
		||||
		socketPath,
 | 
			
		||||
	}
 | 
			
		||||
	// send message
 | 
			
		||||
	ok, _, err := c.SendRequest("streamlocal-forward@openssh.com", true, Marshal(&m))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("ssh: streamlocal-forward@openssh.com request denied by peer")
 | 
			
		||||
	}
 | 
			
		||||
	ch := c.forwards.add(&net.UnixAddr{Name: socketPath, Net: "unix"})
 | 
			
		||||
 | 
			
		||||
	return &unixListener{socketPath, c, ch}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) dialStreamLocal(socketPath string) (Channel, error) {
 | 
			
		||||
	msg := streamLocalChannelOpenDirectMsg{
 | 
			
		||||
		socketPath: socketPath,
 | 
			
		||||
	}
 | 
			
		||||
	ch, in, err := c.OpenChannel("direct-streamlocal@openssh.com", Marshal(&msg))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	go DiscardRequests(in)
 | 
			
		||||
	return ch, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type unixListener struct {
 | 
			
		||||
	socketPath string
 | 
			
		||||
 | 
			
		||||
	conn *Client
 | 
			
		||||
	in   <-chan forward
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Accept waits for and returns the next connection to the listener.
 | 
			
		||||
func (l *unixListener) Accept() (net.Conn, error) {
 | 
			
		||||
	s, ok := <-l.in
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, io.EOF
 | 
			
		||||
	}
 | 
			
		||||
	ch, incoming, err := s.newCh.Accept()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	go DiscardRequests(incoming)
 | 
			
		||||
 | 
			
		||||
	return &chanConn{
 | 
			
		||||
		Channel: ch,
 | 
			
		||||
		laddr: &net.UnixAddr{
 | 
			
		||||
			Name: l.socketPath,
 | 
			
		||||
			Net:  "unix",
 | 
			
		||||
		},
 | 
			
		||||
		raddr: &net.UnixAddr{
 | 
			
		||||
			Name: "@",
 | 
			
		||||
			Net:  "unix",
 | 
			
		||||
		},
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close closes the listener.
 | 
			
		||||
func (l *unixListener) Close() error {
 | 
			
		||||
	// this also closes the listener.
 | 
			
		||||
	l.conn.forwards.remove(&net.UnixAddr{Name: l.socketPath, Net: "unix"})
 | 
			
		||||
	m := streamLocalChannelForwardMsg{
 | 
			
		||||
		l.socketPath,
 | 
			
		||||
	}
 | 
			
		||||
	ok, _, err := l.conn.SendRequest("cancel-streamlocal-forward@openssh.com", true, Marshal(&m))
 | 
			
		||||
	if err == nil && !ok {
 | 
			
		||||
		err = errors.New("ssh: cancel-streamlocal-forward@openssh.com failed")
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Addr returns the listener's network address.
 | 
			
		||||
func (l *unixListener) Addr() net.Addr {
 | 
			
		||||
	return &net.UnixAddr{
 | 
			
		||||
		Name: l.socketPath,
 | 
			
		||||
		Net:  "unix",
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										465
									
								
								vendor/golang.org/x/crypto/ssh/tcpip.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										465
									
								
								vendor/golang.org/x/crypto/ssh/tcpip.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,465 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Listen requests the remote peer open a listening socket on
 | 
			
		||||
// addr. Incoming connections will be available by calling Accept on
 | 
			
		||||
// the returned net.Listener. The listener must be serviced, or the
 | 
			
		||||
// SSH connection may hang.
 | 
			
		||||
// N must be "tcp", "tcp4", "tcp6", or "unix".
 | 
			
		||||
func (c *Client) Listen(n, addr string) (net.Listener, error) {
 | 
			
		||||
	switch n {
 | 
			
		||||
	case "tcp", "tcp4", "tcp6":
 | 
			
		||||
		laddr, err := net.ResolveTCPAddr(n, addr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		return c.ListenTCP(laddr)
 | 
			
		||||
	case "unix":
 | 
			
		||||
		return c.ListenUnix(addr)
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("ssh: unsupported protocol: %s", n)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Automatic port allocation is broken with OpenSSH before 6.0. See
 | 
			
		||||
// also https://bugzilla.mindrot.org/show_bug.cgi?id=2017.  In
 | 
			
		||||
// particular, OpenSSH 5.9 sends a channelOpenMsg with port number 0,
 | 
			
		||||
// rather than the actual port number. This means you can never open
 | 
			
		||||
// two different listeners with auto allocated ports. We work around
 | 
			
		||||
// this by trying explicit ports until we succeed.
 | 
			
		||||
 | 
			
		||||
const openSSHPrefix = "OpenSSH_"
 | 
			
		||||
 | 
			
		||||
var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano()))
 | 
			
		||||
 | 
			
		||||
// isBrokenOpenSSHVersion returns true if the given version string
 | 
			
		||||
// specifies a version of OpenSSH that is known to have a bug in port
 | 
			
		||||
// forwarding.
 | 
			
		||||
func isBrokenOpenSSHVersion(versionStr string) bool {
 | 
			
		||||
	i := strings.Index(versionStr, openSSHPrefix)
 | 
			
		||||
	if i < 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	i += len(openSSHPrefix)
 | 
			
		||||
	j := i
 | 
			
		||||
	for ; j < len(versionStr); j++ {
 | 
			
		||||
		if versionStr[j] < '0' || versionStr[j] > '9' {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	version, _ := strconv.Atoi(versionStr[i:j])
 | 
			
		||||
	return version < 6
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// autoPortListenWorkaround simulates automatic port allocation by
 | 
			
		||||
// trying random ports repeatedly.
 | 
			
		||||
func (c *Client) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) {
 | 
			
		||||
	var sshListener net.Listener
 | 
			
		||||
	var err error
 | 
			
		||||
	const tries = 10
 | 
			
		||||
	for i := 0; i < tries; i++ {
 | 
			
		||||
		addr := *laddr
 | 
			
		||||
		addr.Port = 1024 + portRandomizer.Intn(60000)
 | 
			
		||||
		sshListener, err = c.ListenTCP(&addr)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			laddr.Port = addr.Port
 | 
			
		||||
			return sshListener, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, fmt.Errorf("ssh: listen on random port failed after %d tries: %v", tries, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 4254 7.1
 | 
			
		||||
type channelForwardMsg struct {
 | 
			
		||||
	addr  string
 | 
			
		||||
	rport uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListenTCP requests the remote peer open a listening socket
 | 
			
		||||
// on laddr. Incoming connections will be available by calling
 | 
			
		||||
// Accept on the returned net.Listener.
 | 
			
		||||
func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
 | 
			
		||||
	if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) {
 | 
			
		||||
		return c.autoPortListenWorkaround(laddr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m := channelForwardMsg{
 | 
			
		||||
		laddr.IP.String(),
 | 
			
		||||
		uint32(laddr.Port),
 | 
			
		||||
	}
 | 
			
		||||
	// send message
 | 
			
		||||
	ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("ssh: tcpip-forward request denied by peer")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If the original port was 0, then the remote side will
 | 
			
		||||
	// supply a real port number in the response.
 | 
			
		||||
	if laddr.Port == 0 {
 | 
			
		||||
		var p struct {
 | 
			
		||||
			Port uint32
 | 
			
		||||
		}
 | 
			
		||||
		if err := Unmarshal(resp, &p); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		laddr.Port = int(p.Port)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Register this forward, using the port number we obtained.
 | 
			
		||||
	ch := c.forwards.add(laddr)
 | 
			
		||||
 | 
			
		||||
	return &tcpListener{laddr, c, ch}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// forwardList stores a mapping between remote
 | 
			
		||||
// forward requests and the tcpListeners.
 | 
			
		||||
type forwardList struct {
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
	entries []forwardEntry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// forwardEntry represents an established mapping of a laddr on a
 | 
			
		||||
// remote ssh server to a channel connected to a tcpListener.
 | 
			
		||||
type forwardEntry struct {
 | 
			
		||||
	laddr net.Addr
 | 
			
		||||
	c     chan forward
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// forward represents an incoming forwarded tcpip connection. The
 | 
			
		||||
// arguments to add/remove/lookup should be address as specified in
 | 
			
		||||
// the original forward-request.
 | 
			
		||||
type forward struct {
 | 
			
		||||
	newCh NewChannel // the ssh client channel underlying this forward
 | 
			
		||||
	raddr net.Addr   // the raddr of the incoming connection
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *forwardList) add(addr net.Addr) chan forward {
 | 
			
		||||
	l.Lock()
 | 
			
		||||
	defer l.Unlock()
 | 
			
		||||
	f := forwardEntry{
 | 
			
		||||
		laddr: addr,
 | 
			
		||||
		c:     make(chan forward, 1),
 | 
			
		||||
	}
 | 
			
		||||
	l.entries = append(l.entries, f)
 | 
			
		||||
	return f.c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See RFC 4254, section 7.2
 | 
			
		||||
type forwardedTCPPayload struct {
 | 
			
		||||
	Addr       string
 | 
			
		||||
	Port       uint32
 | 
			
		||||
	OriginAddr string
 | 
			
		||||
	OriginPort uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseTCPAddr parses the originating address from the remote into a *net.TCPAddr.
 | 
			
		||||
func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) {
 | 
			
		||||
	if port == 0 || port > 65535 {
 | 
			
		||||
		return nil, fmt.Errorf("ssh: port number out of range: %d", port)
 | 
			
		||||
	}
 | 
			
		||||
	ip := net.ParseIP(string(addr))
 | 
			
		||||
	if ip == nil {
 | 
			
		||||
		return nil, fmt.Errorf("ssh: cannot parse IP address %q", addr)
 | 
			
		||||
	}
 | 
			
		||||
	return &net.TCPAddr{IP: ip, Port: int(port)}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *forwardList) handleChannels(in <-chan NewChannel) {
 | 
			
		||||
	for ch := range in {
 | 
			
		||||
		var (
 | 
			
		||||
			laddr net.Addr
 | 
			
		||||
			raddr net.Addr
 | 
			
		||||
			err   error
 | 
			
		||||
		)
 | 
			
		||||
		switch channelType := ch.ChannelType(); channelType {
 | 
			
		||||
		case "forwarded-tcpip":
 | 
			
		||||
			var payload forwardedTCPPayload
 | 
			
		||||
			if err = Unmarshal(ch.ExtraData(), &payload); err != nil {
 | 
			
		||||
				ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error())
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// RFC 4254 section 7.2 specifies that incoming
 | 
			
		||||
			// addresses should list the address, in string
 | 
			
		||||
			// format. It is implied that this should be an IP
 | 
			
		||||
			// address, as it would be impossible to connect to it
 | 
			
		||||
			// otherwise.
 | 
			
		||||
			laddr, err = parseTCPAddr(payload.Addr, payload.Port)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ch.Reject(ConnectionFailed, err.Error())
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			raddr, err = parseTCPAddr(payload.OriginAddr, payload.OriginPort)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ch.Reject(ConnectionFailed, err.Error())
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case "forwarded-streamlocal@openssh.com":
 | 
			
		||||
			var payload forwardedStreamLocalPayload
 | 
			
		||||
			if err = Unmarshal(ch.ExtraData(), &payload); err != nil {
 | 
			
		||||
				ch.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+err.Error())
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			laddr = &net.UnixAddr{
 | 
			
		||||
				Name: payload.SocketPath,
 | 
			
		||||
				Net:  "unix",
 | 
			
		||||
			}
 | 
			
		||||
			raddr = &net.UnixAddr{
 | 
			
		||||
				Name: "@",
 | 
			
		||||
				Net:  "unix",
 | 
			
		||||
			}
 | 
			
		||||
		default:
 | 
			
		||||
			panic(fmt.Errorf("ssh: unknown channel type %s", channelType))
 | 
			
		||||
		}
 | 
			
		||||
		if ok := l.forward(laddr, raddr, ch); !ok {
 | 
			
		||||
			// Section 7.2, implementations MUST reject spurious incoming
 | 
			
		||||
			// connections.
 | 
			
		||||
			ch.Reject(Prohibited, "no forward for address")
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// remove removes the forward entry, and the channel feeding its
 | 
			
		||||
// listener.
 | 
			
		||||
func (l *forwardList) remove(addr net.Addr) {
 | 
			
		||||
	l.Lock()
 | 
			
		||||
	defer l.Unlock()
 | 
			
		||||
	for i, f := range l.entries {
 | 
			
		||||
		if addr.Network() == f.laddr.Network() && addr.String() == f.laddr.String() {
 | 
			
		||||
			l.entries = append(l.entries[:i], l.entries[i+1:]...)
 | 
			
		||||
			close(f.c)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// closeAll closes and clears all forwards.
 | 
			
		||||
func (l *forwardList) closeAll() {
 | 
			
		||||
	l.Lock()
 | 
			
		||||
	defer l.Unlock()
 | 
			
		||||
	for _, f := range l.entries {
 | 
			
		||||
		close(f.c)
 | 
			
		||||
	}
 | 
			
		||||
	l.entries = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool {
 | 
			
		||||
	l.Lock()
 | 
			
		||||
	defer l.Unlock()
 | 
			
		||||
	for _, f := range l.entries {
 | 
			
		||||
		if laddr.Network() == f.laddr.Network() && laddr.String() == f.laddr.String() {
 | 
			
		||||
			f.c <- forward{newCh: ch, raddr: raddr}
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type tcpListener struct {
 | 
			
		||||
	laddr *net.TCPAddr
 | 
			
		||||
 | 
			
		||||
	conn *Client
 | 
			
		||||
	in   <-chan forward
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Accept waits for and returns the next connection to the listener.
 | 
			
		||||
func (l *tcpListener) Accept() (net.Conn, error) {
 | 
			
		||||
	s, ok := <-l.in
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, io.EOF
 | 
			
		||||
	}
 | 
			
		||||
	ch, incoming, err := s.newCh.Accept()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	go DiscardRequests(incoming)
 | 
			
		||||
 | 
			
		||||
	return &chanConn{
 | 
			
		||||
		Channel: ch,
 | 
			
		||||
		laddr:   l.laddr,
 | 
			
		||||
		raddr:   s.raddr,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close closes the listener.
 | 
			
		||||
func (l *tcpListener) Close() error {
 | 
			
		||||
	m := channelForwardMsg{
 | 
			
		||||
		l.laddr.IP.String(),
 | 
			
		||||
		uint32(l.laddr.Port),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// this also closes the listener.
 | 
			
		||||
	l.conn.forwards.remove(l.laddr)
 | 
			
		||||
	ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m))
 | 
			
		||||
	if err == nil && !ok {
 | 
			
		||||
		err = errors.New("ssh: cancel-tcpip-forward failed")
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Addr returns the listener's network address.
 | 
			
		||||
func (l *tcpListener) Addr() net.Addr {
 | 
			
		||||
	return l.laddr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dial initiates a connection to the addr from the remote host.
 | 
			
		||||
// The resulting connection has a zero LocalAddr() and RemoteAddr().
 | 
			
		||||
func (c *Client) Dial(n, addr string) (net.Conn, error) {
 | 
			
		||||
	var ch Channel
 | 
			
		||||
	switch n {
 | 
			
		||||
	case "tcp", "tcp4", "tcp6":
 | 
			
		||||
		// Parse the address into host and numeric port.
 | 
			
		||||
		host, portString, err := net.SplitHostPort(addr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		port, err := strconv.ParseUint(portString, 10, 16)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		ch, err = c.dial(net.IPv4zero.String(), 0, host, int(port))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		// Use a zero address for local and remote address.
 | 
			
		||||
		zeroAddr := &net.TCPAddr{
 | 
			
		||||
			IP:   net.IPv4zero,
 | 
			
		||||
			Port: 0,
 | 
			
		||||
		}
 | 
			
		||||
		return &chanConn{
 | 
			
		||||
			Channel: ch,
 | 
			
		||||
			laddr:   zeroAddr,
 | 
			
		||||
			raddr:   zeroAddr,
 | 
			
		||||
		}, nil
 | 
			
		||||
	case "unix":
 | 
			
		||||
		var err error
 | 
			
		||||
		ch, err = c.dialStreamLocal(addr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		return &chanConn{
 | 
			
		||||
			Channel: ch,
 | 
			
		||||
			laddr: &net.UnixAddr{
 | 
			
		||||
				Name: "@",
 | 
			
		||||
				Net:  "unix",
 | 
			
		||||
			},
 | 
			
		||||
			raddr: &net.UnixAddr{
 | 
			
		||||
				Name: addr,
 | 
			
		||||
				Net:  "unix",
 | 
			
		||||
			},
 | 
			
		||||
		}, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("ssh: unsupported protocol: %s", n)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DialTCP connects to the remote address raddr on the network net,
 | 
			
		||||
// which must be "tcp", "tcp4", or "tcp6".  If laddr is not nil, it is used
 | 
			
		||||
// as the local address for the connection.
 | 
			
		||||
func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) {
 | 
			
		||||
	if laddr == nil {
 | 
			
		||||
		laddr = &net.TCPAddr{
 | 
			
		||||
			IP:   net.IPv4zero,
 | 
			
		||||
			Port: 0,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &chanConn{
 | 
			
		||||
		Channel: ch,
 | 
			
		||||
		laddr:   laddr,
 | 
			
		||||
		raddr:   raddr,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 4254 7.2
 | 
			
		||||
type channelOpenDirectMsg struct {
 | 
			
		||||
	raddr string
 | 
			
		||||
	rport uint32
 | 
			
		||||
	laddr string
 | 
			
		||||
	lport uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel, error) {
 | 
			
		||||
	msg := channelOpenDirectMsg{
 | 
			
		||||
		raddr: raddr,
 | 
			
		||||
		rport: uint32(rport),
 | 
			
		||||
		laddr: laddr,
 | 
			
		||||
		lport: uint32(lport),
 | 
			
		||||
	}
 | 
			
		||||
	ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	go DiscardRequests(in)
 | 
			
		||||
	return ch, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type tcpChan struct {
 | 
			
		||||
	Channel // the backing channel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// chanConn fulfills the net.Conn interface without
 | 
			
		||||
// the tcpChan having to hold laddr or raddr directly.
 | 
			
		||||
type chanConn struct {
 | 
			
		||||
	Channel
 | 
			
		||||
	laddr, raddr net.Addr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LocalAddr returns the local network address.
 | 
			
		||||
func (t *chanConn) LocalAddr() net.Addr {
 | 
			
		||||
	return t.laddr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoteAddr returns the remote network address.
 | 
			
		||||
func (t *chanConn) RemoteAddr() net.Addr {
 | 
			
		||||
	return t.raddr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetDeadline sets the read and write deadlines associated
 | 
			
		||||
// with the connection.
 | 
			
		||||
func (t *chanConn) SetDeadline(deadline time.Time) error {
 | 
			
		||||
	if err := t.SetReadDeadline(deadline); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return t.SetWriteDeadline(deadline)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetReadDeadline sets the read deadline.
 | 
			
		||||
// A zero value for t means Read will not time out.
 | 
			
		||||
// After the deadline, the error from Read will implement net.Error
 | 
			
		||||
// with Timeout() == true.
 | 
			
		||||
func (t *chanConn) SetReadDeadline(deadline time.Time) error {
 | 
			
		||||
	// for compatibility with previous version,
 | 
			
		||||
	// the error message contains "tcpChan"
 | 
			
		||||
	return errors.New("ssh: tcpChan: deadline not supported")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetWriteDeadline exists to satisfy the net.Conn interface
 | 
			
		||||
// but is not implemented by this type.  It always returns an error.
 | 
			
		||||
func (t *chanConn) SetWriteDeadline(deadline time.Time) error {
 | 
			
		||||
	return errors.New("ssh: tcpChan: deadline not supported")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										951
									
								
								vendor/golang.org/x/crypto/ssh/terminal/terminal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										951
									
								
								vendor/golang.org/x/crypto/ssh/terminal/terminal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,951 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package terminal
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"io"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// EscapeCodes contains escape sequences that can be written to the terminal in
 | 
			
		||||
// order to achieve different styles of text.
 | 
			
		||||
type EscapeCodes struct {
 | 
			
		||||
	// Foreground colors
 | 
			
		||||
	Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
 | 
			
		||||
 | 
			
		||||
	// Reset all attributes
 | 
			
		||||
	Reset []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var vt100EscapeCodes = EscapeCodes{
 | 
			
		||||
	Black:   []byte{keyEscape, '[', '3', '0', 'm'},
 | 
			
		||||
	Red:     []byte{keyEscape, '[', '3', '1', 'm'},
 | 
			
		||||
	Green:   []byte{keyEscape, '[', '3', '2', 'm'},
 | 
			
		||||
	Yellow:  []byte{keyEscape, '[', '3', '3', 'm'},
 | 
			
		||||
	Blue:    []byte{keyEscape, '[', '3', '4', 'm'},
 | 
			
		||||
	Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
 | 
			
		||||
	Cyan:    []byte{keyEscape, '[', '3', '6', 'm'},
 | 
			
		||||
	White:   []byte{keyEscape, '[', '3', '7', 'm'},
 | 
			
		||||
 | 
			
		||||
	Reset: []byte{keyEscape, '[', '0', 'm'},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Terminal contains the state for running a VT100 terminal that is capable of
 | 
			
		||||
// reading lines of input.
 | 
			
		||||
type Terminal struct {
 | 
			
		||||
	// AutoCompleteCallback, if non-null, is called for each keypress with
 | 
			
		||||
	// the full input line and the current position of the cursor (in
 | 
			
		||||
	// bytes, as an index into |line|). If it returns ok=false, the key
 | 
			
		||||
	// press is processed normally. Otherwise it returns a replacement line
 | 
			
		||||
	// and the new cursor position.
 | 
			
		||||
	AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
 | 
			
		||||
 | 
			
		||||
	// Escape contains a pointer to the escape codes for this terminal.
 | 
			
		||||
	// It's always a valid pointer, although the escape codes themselves
 | 
			
		||||
	// may be empty if the terminal doesn't support them.
 | 
			
		||||
	Escape *EscapeCodes
 | 
			
		||||
 | 
			
		||||
	// lock protects the terminal and the state in this object from
 | 
			
		||||
	// concurrent processing of a key press and a Write() call.
 | 
			
		||||
	lock sync.Mutex
 | 
			
		||||
 | 
			
		||||
	c      io.ReadWriter
 | 
			
		||||
	prompt []rune
 | 
			
		||||
 | 
			
		||||
	// line is the current line being entered.
 | 
			
		||||
	line []rune
 | 
			
		||||
	// pos is the logical position of the cursor in line
 | 
			
		||||
	pos int
 | 
			
		||||
	// echo is true if local echo is enabled
 | 
			
		||||
	echo bool
 | 
			
		||||
	// pasteActive is true iff there is a bracketed paste operation in
 | 
			
		||||
	// progress.
 | 
			
		||||
	pasteActive bool
 | 
			
		||||
 | 
			
		||||
	// cursorX contains the current X value of the cursor where the left
 | 
			
		||||
	// edge is 0. cursorY contains the row number where the first row of
 | 
			
		||||
	// the current line is 0.
 | 
			
		||||
	cursorX, cursorY int
 | 
			
		||||
	// maxLine is the greatest value of cursorY so far.
 | 
			
		||||
	maxLine int
 | 
			
		||||
 | 
			
		||||
	termWidth, termHeight int
 | 
			
		||||
 | 
			
		||||
	// outBuf contains the terminal data to be sent.
 | 
			
		||||
	outBuf []byte
 | 
			
		||||
	// remainder contains the remainder of any partial key sequences after
 | 
			
		||||
	// a read. It aliases into inBuf.
 | 
			
		||||
	remainder []byte
 | 
			
		||||
	inBuf     [256]byte
 | 
			
		||||
 | 
			
		||||
	// history contains previously entered commands so that they can be
 | 
			
		||||
	// accessed with the up and down keys.
 | 
			
		||||
	history stRingBuffer
 | 
			
		||||
	// historyIndex stores the currently accessed history entry, where zero
 | 
			
		||||
	// means the immediately previous entry.
 | 
			
		||||
	historyIndex int
 | 
			
		||||
	// When navigating up and down the history it's possible to return to
 | 
			
		||||
	// the incomplete, initial line. That value is stored in
 | 
			
		||||
	// historyPending.
 | 
			
		||||
	historyPending string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
 | 
			
		||||
// a local terminal, that terminal must first have been put into raw mode.
 | 
			
		||||
// prompt is a string that is written at the start of each input line (i.e.
 | 
			
		||||
// "> ").
 | 
			
		||||
func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
 | 
			
		||||
	return &Terminal{
 | 
			
		||||
		Escape:       &vt100EscapeCodes,
 | 
			
		||||
		c:            c,
 | 
			
		||||
		prompt:       []rune(prompt),
 | 
			
		||||
		termWidth:    80,
 | 
			
		||||
		termHeight:   24,
 | 
			
		||||
		echo:         true,
 | 
			
		||||
		historyIndex: -1,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	keyCtrlD     = 4
 | 
			
		||||
	keyCtrlU     = 21
 | 
			
		||||
	keyEnter     = '\r'
 | 
			
		||||
	keyEscape    = 27
 | 
			
		||||
	keyBackspace = 127
 | 
			
		||||
	keyUnknown   = 0xd800 /* UTF-16 surrogate area */ + iota
 | 
			
		||||
	keyUp
 | 
			
		||||
	keyDown
 | 
			
		||||
	keyLeft
 | 
			
		||||
	keyRight
 | 
			
		||||
	keyAltLeft
 | 
			
		||||
	keyAltRight
 | 
			
		||||
	keyHome
 | 
			
		||||
	keyEnd
 | 
			
		||||
	keyDeleteWord
 | 
			
		||||
	keyDeleteLine
 | 
			
		||||
	keyClearScreen
 | 
			
		||||
	keyPasteStart
 | 
			
		||||
	keyPasteEnd
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	crlf       = []byte{'\r', '\n'}
 | 
			
		||||
	pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
 | 
			
		||||
	pasteEnd   = []byte{keyEscape, '[', '2', '0', '1', '~'}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// bytesToKey tries to parse a key sequence from b. If successful, it returns
 | 
			
		||||
// the key and the remainder of the input. Otherwise it returns utf8.RuneError.
 | 
			
		||||
func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
 | 
			
		||||
	if len(b) == 0 {
 | 
			
		||||
		return utf8.RuneError, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !pasteActive {
 | 
			
		||||
		switch b[0] {
 | 
			
		||||
		case 1: // ^A
 | 
			
		||||
			return keyHome, b[1:]
 | 
			
		||||
		case 5: // ^E
 | 
			
		||||
			return keyEnd, b[1:]
 | 
			
		||||
		case 8: // ^H
 | 
			
		||||
			return keyBackspace, b[1:]
 | 
			
		||||
		case 11: // ^K
 | 
			
		||||
			return keyDeleteLine, b[1:]
 | 
			
		||||
		case 12: // ^L
 | 
			
		||||
			return keyClearScreen, b[1:]
 | 
			
		||||
		case 23: // ^W
 | 
			
		||||
			return keyDeleteWord, b[1:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b[0] != keyEscape {
 | 
			
		||||
		if !utf8.FullRune(b) {
 | 
			
		||||
			return utf8.RuneError, b
 | 
			
		||||
		}
 | 
			
		||||
		r, l := utf8.DecodeRune(b)
 | 
			
		||||
		return r, b[l:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
 | 
			
		||||
		switch b[2] {
 | 
			
		||||
		case 'A':
 | 
			
		||||
			return keyUp, b[3:]
 | 
			
		||||
		case 'B':
 | 
			
		||||
			return keyDown, b[3:]
 | 
			
		||||
		case 'C':
 | 
			
		||||
			return keyRight, b[3:]
 | 
			
		||||
		case 'D':
 | 
			
		||||
			return keyLeft, b[3:]
 | 
			
		||||
		case 'H':
 | 
			
		||||
			return keyHome, b[3:]
 | 
			
		||||
		case 'F':
 | 
			
		||||
			return keyEnd, b[3:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
 | 
			
		||||
		switch b[5] {
 | 
			
		||||
		case 'C':
 | 
			
		||||
			return keyAltRight, b[6:]
 | 
			
		||||
		case 'D':
 | 
			
		||||
			return keyAltLeft, b[6:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) {
 | 
			
		||||
		return keyPasteStart, b[6:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) {
 | 
			
		||||
		return keyPasteEnd, b[6:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we get here then we have a key that we don't recognise, or a
 | 
			
		||||
	// partial sequence. It's not clear how one should find the end of a
 | 
			
		||||
	// sequence without knowing them all, but it seems that [a-zA-Z~] only
 | 
			
		||||
	// appears at the end of a sequence.
 | 
			
		||||
	for i, c := range b[0:] {
 | 
			
		||||
		if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' {
 | 
			
		||||
			return keyUnknown, b[i+1:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return utf8.RuneError, b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// queue appends data to the end of t.outBuf
 | 
			
		||||
func (t *Terminal) queue(data []rune) {
 | 
			
		||||
	t.outBuf = append(t.outBuf, []byte(string(data))...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'}
 | 
			
		||||
var space = []rune{' '}
 | 
			
		||||
 | 
			
		||||
func isPrintable(key rune) bool {
 | 
			
		||||
	isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
 | 
			
		||||
	return key >= 32 && !isInSurrogateArea
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// moveCursorToPos appends data to t.outBuf which will move the cursor to the
 | 
			
		||||
// given, logical position in the text.
 | 
			
		||||
func (t *Terminal) moveCursorToPos(pos int) {
 | 
			
		||||
	if !t.echo {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	x := visualLength(t.prompt) + pos
 | 
			
		||||
	y := x / t.termWidth
 | 
			
		||||
	x = x % t.termWidth
 | 
			
		||||
 | 
			
		||||
	up := 0
 | 
			
		||||
	if y < t.cursorY {
 | 
			
		||||
		up = t.cursorY - y
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	down := 0
 | 
			
		||||
	if y > t.cursorY {
 | 
			
		||||
		down = y - t.cursorY
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	left := 0
 | 
			
		||||
	if x < t.cursorX {
 | 
			
		||||
		left = t.cursorX - x
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	right := 0
 | 
			
		||||
	if x > t.cursorX {
 | 
			
		||||
		right = x - t.cursorX
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.cursorX = x
 | 
			
		||||
	t.cursorY = y
 | 
			
		||||
	t.move(up, down, left, right)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) move(up, down, left, right int) {
 | 
			
		||||
	movement := make([]rune, 3*(up+down+left+right))
 | 
			
		||||
	m := movement
 | 
			
		||||
	for i := 0; i < up; i++ {
 | 
			
		||||
		m[0] = keyEscape
 | 
			
		||||
		m[1] = '['
 | 
			
		||||
		m[2] = 'A'
 | 
			
		||||
		m = m[3:]
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < down; i++ {
 | 
			
		||||
		m[0] = keyEscape
 | 
			
		||||
		m[1] = '['
 | 
			
		||||
		m[2] = 'B'
 | 
			
		||||
		m = m[3:]
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < left; i++ {
 | 
			
		||||
		m[0] = keyEscape
 | 
			
		||||
		m[1] = '['
 | 
			
		||||
		m[2] = 'D'
 | 
			
		||||
		m = m[3:]
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < right; i++ {
 | 
			
		||||
		m[0] = keyEscape
 | 
			
		||||
		m[1] = '['
 | 
			
		||||
		m[2] = 'C'
 | 
			
		||||
		m = m[3:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.queue(movement)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) clearLineToRight() {
 | 
			
		||||
	op := []rune{keyEscape, '[', 'K'}
 | 
			
		||||
	t.queue(op)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const maxLineLength = 4096
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) setLine(newLine []rune, newPos int) {
 | 
			
		||||
	if t.echo {
 | 
			
		||||
		t.moveCursorToPos(0)
 | 
			
		||||
		t.writeLine(newLine)
 | 
			
		||||
		for i := len(newLine); i < len(t.line); i++ {
 | 
			
		||||
			t.writeLine(space)
 | 
			
		||||
		}
 | 
			
		||||
		t.moveCursorToPos(newPos)
 | 
			
		||||
	}
 | 
			
		||||
	t.line = newLine
 | 
			
		||||
	t.pos = newPos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) advanceCursor(places int) {
 | 
			
		||||
	t.cursorX += places
 | 
			
		||||
	t.cursorY += t.cursorX / t.termWidth
 | 
			
		||||
	if t.cursorY > t.maxLine {
 | 
			
		||||
		t.maxLine = t.cursorY
 | 
			
		||||
	}
 | 
			
		||||
	t.cursorX = t.cursorX % t.termWidth
 | 
			
		||||
 | 
			
		||||
	if places > 0 && t.cursorX == 0 {
 | 
			
		||||
		// Normally terminals will advance the current position
 | 
			
		||||
		// when writing a character. But that doesn't happen
 | 
			
		||||
		// for the last character in a line. However, when
 | 
			
		||||
		// writing a character (except a new line) that causes
 | 
			
		||||
		// a line wrap, the position will be advanced two
 | 
			
		||||
		// places.
 | 
			
		||||
		//
 | 
			
		||||
		// So, if we are stopping at the end of a line, we
 | 
			
		||||
		// need to write a newline so that our cursor can be
 | 
			
		||||
		// advanced to the next line.
 | 
			
		||||
		t.outBuf = append(t.outBuf, '\r', '\n')
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) eraseNPreviousChars(n int) {
 | 
			
		||||
	if n == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t.pos < n {
 | 
			
		||||
		n = t.pos
 | 
			
		||||
	}
 | 
			
		||||
	t.pos -= n
 | 
			
		||||
	t.moveCursorToPos(t.pos)
 | 
			
		||||
 | 
			
		||||
	copy(t.line[t.pos:], t.line[n+t.pos:])
 | 
			
		||||
	t.line = t.line[:len(t.line)-n]
 | 
			
		||||
	if t.echo {
 | 
			
		||||
		t.writeLine(t.line[t.pos:])
 | 
			
		||||
		for i := 0; i < n; i++ {
 | 
			
		||||
			t.queue(space)
 | 
			
		||||
		}
 | 
			
		||||
		t.advanceCursor(n)
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// countToLeftWord returns then number of characters from the cursor to the
 | 
			
		||||
// start of the previous word.
 | 
			
		||||
func (t *Terminal) countToLeftWord() int {
 | 
			
		||||
	if t.pos == 0 {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pos := t.pos - 1
 | 
			
		||||
	for pos > 0 {
 | 
			
		||||
		if t.line[pos] != ' ' {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		pos--
 | 
			
		||||
	}
 | 
			
		||||
	for pos > 0 {
 | 
			
		||||
		if t.line[pos] == ' ' {
 | 
			
		||||
			pos++
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		pos--
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return t.pos - pos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// countToRightWord returns then number of characters from the cursor to the
 | 
			
		||||
// start of the next word.
 | 
			
		||||
func (t *Terminal) countToRightWord() int {
 | 
			
		||||
	pos := t.pos
 | 
			
		||||
	for pos < len(t.line) {
 | 
			
		||||
		if t.line[pos] == ' ' {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		pos++
 | 
			
		||||
	}
 | 
			
		||||
	for pos < len(t.line) {
 | 
			
		||||
		if t.line[pos] != ' ' {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		pos++
 | 
			
		||||
	}
 | 
			
		||||
	return pos - t.pos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// visualLength returns the number of visible glyphs in s.
 | 
			
		||||
func visualLength(runes []rune) int {
 | 
			
		||||
	inEscapeSeq := false
 | 
			
		||||
	length := 0
 | 
			
		||||
 | 
			
		||||
	for _, r := range runes {
 | 
			
		||||
		switch {
 | 
			
		||||
		case inEscapeSeq:
 | 
			
		||||
			if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
 | 
			
		||||
				inEscapeSeq = false
 | 
			
		||||
			}
 | 
			
		||||
		case r == '\x1b':
 | 
			
		||||
			inEscapeSeq = true
 | 
			
		||||
		default:
 | 
			
		||||
			length++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return length
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleKey processes the given key and, optionally, returns a line of text
 | 
			
		||||
// that the user has entered.
 | 
			
		||||
func (t *Terminal) handleKey(key rune) (line string, ok bool) {
 | 
			
		||||
	if t.pasteActive && key != keyEnter {
 | 
			
		||||
		t.addKeyToLine(key)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch key {
 | 
			
		||||
	case keyBackspace:
 | 
			
		||||
		if t.pos == 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		t.eraseNPreviousChars(1)
 | 
			
		||||
	case keyAltLeft:
 | 
			
		||||
		// move left by a word.
 | 
			
		||||
		t.pos -= t.countToLeftWord()
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyAltRight:
 | 
			
		||||
		// move right by a word.
 | 
			
		||||
		t.pos += t.countToRightWord()
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyLeft:
 | 
			
		||||
		if t.pos == 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		t.pos--
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyRight:
 | 
			
		||||
		if t.pos == len(t.line) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		t.pos++
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyHome:
 | 
			
		||||
		if t.pos == 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		t.pos = 0
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyEnd:
 | 
			
		||||
		if t.pos == len(t.line) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		t.pos = len(t.line)
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyUp:
 | 
			
		||||
		entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return "", false
 | 
			
		||||
		}
 | 
			
		||||
		if t.historyIndex == -1 {
 | 
			
		||||
			t.historyPending = string(t.line)
 | 
			
		||||
		}
 | 
			
		||||
		t.historyIndex++
 | 
			
		||||
		runes := []rune(entry)
 | 
			
		||||
		t.setLine(runes, len(runes))
 | 
			
		||||
	case keyDown:
 | 
			
		||||
		switch t.historyIndex {
 | 
			
		||||
		case -1:
 | 
			
		||||
			return
 | 
			
		||||
		case 0:
 | 
			
		||||
			runes := []rune(t.historyPending)
 | 
			
		||||
			t.setLine(runes, len(runes))
 | 
			
		||||
			t.historyIndex--
 | 
			
		||||
		default:
 | 
			
		||||
			entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
 | 
			
		||||
			if ok {
 | 
			
		||||
				t.historyIndex--
 | 
			
		||||
				runes := []rune(entry)
 | 
			
		||||
				t.setLine(runes, len(runes))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case keyEnter:
 | 
			
		||||
		t.moveCursorToPos(len(t.line))
 | 
			
		||||
		t.queue([]rune("\r\n"))
 | 
			
		||||
		line = string(t.line)
 | 
			
		||||
		ok = true
 | 
			
		||||
		t.line = t.line[:0]
 | 
			
		||||
		t.pos = 0
 | 
			
		||||
		t.cursorX = 0
 | 
			
		||||
		t.cursorY = 0
 | 
			
		||||
		t.maxLine = 0
 | 
			
		||||
	case keyDeleteWord:
 | 
			
		||||
		// Delete zero or more spaces and then one or more characters.
 | 
			
		||||
		t.eraseNPreviousChars(t.countToLeftWord())
 | 
			
		||||
	case keyDeleteLine:
 | 
			
		||||
		// Delete everything from the current cursor position to the
 | 
			
		||||
		// end of line.
 | 
			
		||||
		for i := t.pos; i < len(t.line); i++ {
 | 
			
		||||
			t.queue(space)
 | 
			
		||||
			t.advanceCursor(1)
 | 
			
		||||
		}
 | 
			
		||||
		t.line = t.line[:t.pos]
 | 
			
		||||
		t.moveCursorToPos(t.pos)
 | 
			
		||||
	case keyCtrlD:
 | 
			
		||||
		// Erase the character under the current position.
 | 
			
		||||
		// The EOF case when the line is empty is handled in
 | 
			
		||||
		// readLine().
 | 
			
		||||
		if t.pos < len(t.line) {
 | 
			
		||||
			t.pos++
 | 
			
		||||
			t.eraseNPreviousChars(1)
 | 
			
		||||
		}
 | 
			
		||||
	case keyCtrlU:
 | 
			
		||||
		t.eraseNPreviousChars(t.pos)
 | 
			
		||||
	case keyClearScreen:
 | 
			
		||||
		// Erases the screen and moves the cursor to the home position.
 | 
			
		||||
		t.queue([]rune("\x1b[2J\x1b[H"))
 | 
			
		||||
		t.queue(t.prompt)
 | 
			
		||||
		t.cursorX, t.cursorY = 0, 0
 | 
			
		||||
		t.advanceCursor(visualLength(t.prompt))
 | 
			
		||||
		t.setLine(t.line, t.pos)
 | 
			
		||||
	default:
 | 
			
		||||
		if t.AutoCompleteCallback != nil {
 | 
			
		||||
			prefix := string(t.line[:t.pos])
 | 
			
		||||
			suffix := string(t.line[t.pos:])
 | 
			
		||||
 | 
			
		||||
			t.lock.Unlock()
 | 
			
		||||
			newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
 | 
			
		||||
			t.lock.Lock()
 | 
			
		||||
 | 
			
		||||
			if completeOk {
 | 
			
		||||
				t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !isPrintable(key) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if len(t.line) == maxLineLength {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		t.addKeyToLine(key)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// addKeyToLine inserts the given key at the current position in the current
 | 
			
		||||
// line.
 | 
			
		||||
func (t *Terminal) addKeyToLine(key rune) {
 | 
			
		||||
	if len(t.line) == cap(t.line) {
 | 
			
		||||
		newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
 | 
			
		||||
		copy(newLine, t.line)
 | 
			
		||||
		t.line = newLine
 | 
			
		||||
	}
 | 
			
		||||
	t.line = t.line[:len(t.line)+1]
 | 
			
		||||
	copy(t.line[t.pos+1:], t.line[t.pos:])
 | 
			
		||||
	t.line[t.pos] = key
 | 
			
		||||
	if t.echo {
 | 
			
		||||
		t.writeLine(t.line[t.pos:])
 | 
			
		||||
	}
 | 
			
		||||
	t.pos++
 | 
			
		||||
	t.moveCursorToPos(t.pos)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) writeLine(line []rune) {
 | 
			
		||||
	for len(line) != 0 {
 | 
			
		||||
		remainingOnLine := t.termWidth - t.cursorX
 | 
			
		||||
		todo := len(line)
 | 
			
		||||
		if todo > remainingOnLine {
 | 
			
		||||
			todo = remainingOnLine
 | 
			
		||||
		}
 | 
			
		||||
		t.queue(line[:todo])
 | 
			
		||||
		t.advanceCursor(visualLength(line[:todo]))
 | 
			
		||||
		line = line[todo:]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
 | 
			
		||||
func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
 | 
			
		||||
	for len(buf) > 0 {
 | 
			
		||||
		i := bytes.IndexByte(buf, '\n')
 | 
			
		||||
		todo := len(buf)
 | 
			
		||||
		if i >= 0 {
 | 
			
		||||
			todo = i
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var nn int
 | 
			
		||||
		nn, err = w.Write(buf[:todo])
 | 
			
		||||
		n += nn
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return n, err
 | 
			
		||||
		}
 | 
			
		||||
		buf = buf[todo:]
 | 
			
		||||
 | 
			
		||||
		if i >= 0 {
 | 
			
		||||
			if _, err = w.Write(crlf); err != nil {
 | 
			
		||||
				return n, err
 | 
			
		||||
			}
 | 
			
		||||
			n++
 | 
			
		||||
			buf = buf[1:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return n, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) Write(buf []byte) (n int, err error) {
 | 
			
		||||
	t.lock.Lock()
 | 
			
		||||
	defer t.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	if t.cursorX == 0 && t.cursorY == 0 {
 | 
			
		||||
		// This is the easy case: there's nothing on the screen that we
 | 
			
		||||
		// have to move out of the way.
 | 
			
		||||
		return writeWithCRLF(t.c, buf)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We have a prompt and possibly user input on the screen. We
 | 
			
		||||
	// have to clear it first.
 | 
			
		||||
	t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
 | 
			
		||||
	t.cursorX = 0
 | 
			
		||||
	t.clearLineToRight()
 | 
			
		||||
 | 
			
		||||
	for t.cursorY > 0 {
 | 
			
		||||
		t.move(1 /* up */, 0, 0, 0)
 | 
			
		||||
		t.cursorY--
 | 
			
		||||
		t.clearLineToRight()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err = t.c.Write(t.outBuf); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	t.outBuf = t.outBuf[:0]
 | 
			
		||||
 | 
			
		||||
	if n, err = writeWithCRLF(t.c, buf); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.writeLine(t.prompt)
 | 
			
		||||
	if t.echo {
 | 
			
		||||
		t.writeLine(t.line)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.moveCursorToPos(t.pos)
 | 
			
		||||
 | 
			
		||||
	if _, err = t.c.Write(t.outBuf); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	t.outBuf = t.outBuf[:0]
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadPassword temporarily changes the prompt and reads a password, without
 | 
			
		||||
// echo, from the terminal.
 | 
			
		||||
func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
 | 
			
		||||
	t.lock.Lock()
 | 
			
		||||
	defer t.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	oldPrompt := t.prompt
 | 
			
		||||
	t.prompt = []rune(prompt)
 | 
			
		||||
	t.echo = false
 | 
			
		||||
 | 
			
		||||
	line, err = t.readLine()
 | 
			
		||||
 | 
			
		||||
	t.prompt = oldPrompt
 | 
			
		||||
	t.echo = true
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadLine returns a line of input from the terminal.
 | 
			
		||||
func (t *Terminal) ReadLine() (line string, err error) {
 | 
			
		||||
	t.lock.Lock()
 | 
			
		||||
	defer t.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	return t.readLine()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) readLine() (line string, err error) {
 | 
			
		||||
	// t.lock must be held at this point
 | 
			
		||||
 | 
			
		||||
	if t.cursorX == 0 && t.cursorY == 0 {
 | 
			
		||||
		t.writeLine(t.prompt)
 | 
			
		||||
		t.c.Write(t.outBuf)
 | 
			
		||||
		t.outBuf = t.outBuf[:0]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lineIsPasted := t.pasteActive
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		rest := t.remainder
 | 
			
		||||
		lineOk := false
 | 
			
		||||
		for !lineOk {
 | 
			
		||||
			var key rune
 | 
			
		||||
			key, rest = bytesToKey(rest, t.pasteActive)
 | 
			
		||||
			if key == utf8.RuneError {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			if !t.pasteActive {
 | 
			
		||||
				if key == keyCtrlD {
 | 
			
		||||
					if len(t.line) == 0 {
 | 
			
		||||
						return "", io.EOF
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if key == keyPasteStart {
 | 
			
		||||
					t.pasteActive = true
 | 
			
		||||
					if len(t.line) == 0 {
 | 
			
		||||
						lineIsPasted = true
 | 
			
		||||
					}
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			} else if key == keyPasteEnd {
 | 
			
		||||
				t.pasteActive = false
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if !t.pasteActive {
 | 
			
		||||
				lineIsPasted = false
 | 
			
		||||
			}
 | 
			
		||||
			line, lineOk = t.handleKey(key)
 | 
			
		||||
		}
 | 
			
		||||
		if len(rest) > 0 {
 | 
			
		||||
			n := copy(t.inBuf[:], rest)
 | 
			
		||||
			t.remainder = t.inBuf[:n]
 | 
			
		||||
		} else {
 | 
			
		||||
			t.remainder = nil
 | 
			
		||||
		}
 | 
			
		||||
		t.c.Write(t.outBuf)
 | 
			
		||||
		t.outBuf = t.outBuf[:0]
 | 
			
		||||
		if lineOk {
 | 
			
		||||
			if t.echo {
 | 
			
		||||
				t.historyIndex = -1
 | 
			
		||||
				t.history.Add(line)
 | 
			
		||||
			}
 | 
			
		||||
			if lineIsPasted {
 | 
			
		||||
				err = ErrPasteIndicator
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// t.remainder is a slice at the beginning of t.inBuf
 | 
			
		||||
		// containing a partial key sequence
 | 
			
		||||
		readBuf := t.inBuf[len(t.remainder):]
 | 
			
		||||
		var n int
 | 
			
		||||
 | 
			
		||||
		t.lock.Unlock()
 | 
			
		||||
		n, err = t.c.Read(readBuf)
 | 
			
		||||
		t.lock.Lock()
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		t.remainder = t.inBuf[:n+len(t.remainder)]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPrompt sets the prompt to be used when reading subsequent lines.
 | 
			
		||||
func (t *Terminal) SetPrompt(prompt string) {
 | 
			
		||||
	t.lock.Lock()
 | 
			
		||||
	defer t.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	t.prompt = []rune(prompt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
 | 
			
		||||
	// Move cursor to column zero at the start of the line.
 | 
			
		||||
	t.move(t.cursorY, 0, t.cursorX, 0)
 | 
			
		||||
	t.cursorX, t.cursorY = 0, 0
 | 
			
		||||
	t.clearLineToRight()
 | 
			
		||||
	for t.cursorY < numPrevLines {
 | 
			
		||||
		// Move down a line
 | 
			
		||||
		t.move(0, 1, 0, 0)
 | 
			
		||||
		t.cursorY++
 | 
			
		||||
		t.clearLineToRight()
 | 
			
		||||
	}
 | 
			
		||||
	// Move back to beginning.
 | 
			
		||||
	t.move(t.cursorY, 0, 0, 0)
 | 
			
		||||
	t.cursorX, t.cursorY = 0, 0
 | 
			
		||||
 | 
			
		||||
	t.queue(t.prompt)
 | 
			
		||||
	t.advanceCursor(visualLength(t.prompt))
 | 
			
		||||
	t.writeLine(t.line)
 | 
			
		||||
	t.moveCursorToPos(t.pos)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Terminal) SetSize(width, height int) error {
 | 
			
		||||
	t.lock.Lock()
 | 
			
		||||
	defer t.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	if width == 0 {
 | 
			
		||||
		width = 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	oldWidth := t.termWidth
 | 
			
		||||
	t.termWidth, t.termHeight = width, height
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case width == oldWidth:
 | 
			
		||||
		// If the width didn't change then nothing else needs to be
 | 
			
		||||
		// done.
 | 
			
		||||
		return nil
 | 
			
		||||
	case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
 | 
			
		||||
		// If there is nothing on current line and no prompt printed,
 | 
			
		||||
		// just do nothing
 | 
			
		||||
		return nil
 | 
			
		||||
	case width < oldWidth:
 | 
			
		||||
		// Some terminals (e.g. xterm) will truncate lines that were
 | 
			
		||||
		// too long when shinking. Others, (e.g. gnome-terminal) will
 | 
			
		||||
		// attempt to wrap them. For the former, repainting t.maxLine
 | 
			
		||||
		// works great, but that behaviour goes badly wrong in the case
 | 
			
		||||
		// of the latter because they have doubled every full line.
 | 
			
		||||
 | 
			
		||||
		// We assume that we are working on a terminal that wraps lines
 | 
			
		||||
		// and adjust the cursor position based on every previous line
 | 
			
		||||
		// wrapping and turning into two. This causes the prompt on
 | 
			
		||||
		// xterms to move upwards, which isn't great, but it avoids a
 | 
			
		||||
		// huge mess with gnome-terminal.
 | 
			
		||||
		if t.cursorX >= t.termWidth {
 | 
			
		||||
			t.cursorX = t.termWidth - 1
 | 
			
		||||
		}
 | 
			
		||||
		t.cursorY *= 2
 | 
			
		||||
		t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2)
 | 
			
		||||
	case width > oldWidth:
 | 
			
		||||
		// If the terminal expands then our position calculations will
 | 
			
		||||
		// be wrong in the future because we think the cursor is
 | 
			
		||||
		// |t.pos| chars into the string, but there will be a gap at
 | 
			
		||||
		// the end of any wrapped line.
 | 
			
		||||
		//
 | 
			
		||||
		// But the position will actually be correct until we move, so
 | 
			
		||||
		// we can move back to the beginning and repaint everything.
 | 
			
		||||
		t.clearAndRepaintLinePlusNPrevious(t.maxLine)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := t.c.Write(t.outBuf)
 | 
			
		||||
	t.outBuf = t.outBuf[:0]
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type pasteIndicatorError struct{}
 | 
			
		||||
 | 
			
		||||
func (pasteIndicatorError) Error() string {
 | 
			
		||||
	return "terminal: ErrPasteIndicator not correctly handled"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrPasteIndicator may be returned from ReadLine as the error, in addition
 | 
			
		||||
// to valid line data. It indicates that bracketed paste mode is enabled and
 | 
			
		||||
// that the returned line consists only of pasted data. Programs may wish to
 | 
			
		||||
// interpret pasted data more literally than typed data.
 | 
			
		||||
var ErrPasteIndicator = pasteIndicatorError{}
 | 
			
		||||
 | 
			
		||||
// SetBracketedPasteMode requests that the terminal bracket paste operations
 | 
			
		||||
// with markers. Not all terminals support this but, if it is supported, then
 | 
			
		||||
// enabling this mode will stop any autocomplete callback from running due to
 | 
			
		||||
// pastes. Additionally, any lines that are completely pasted will be returned
 | 
			
		||||
// from ReadLine with the error set to ErrPasteIndicator.
 | 
			
		||||
func (t *Terminal) SetBracketedPasteMode(on bool) {
 | 
			
		||||
	if on {
 | 
			
		||||
		io.WriteString(t.c, "\x1b[?2004h")
 | 
			
		||||
	} else {
 | 
			
		||||
		io.WriteString(t.c, "\x1b[?2004l")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// stRingBuffer is a ring buffer of strings.
 | 
			
		||||
type stRingBuffer struct {
 | 
			
		||||
	// entries contains max elements.
 | 
			
		||||
	entries []string
 | 
			
		||||
	max     int
 | 
			
		||||
	// head contains the index of the element most recently added to the ring.
 | 
			
		||||
	head int
 | 
			
		||||
	// size contains the number of elements in the ring.
 | 
			
		||||
	size int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *stRingBuffer) Add(a string) {
 | 
			
		||||
	if s.entries == nil {
 | 
			
		||||
		const defaultNumEntries = 100
 | 
			
		||||
		s.entries = make([]string, defaultNumEntries)
 | 
			
		||||
		s.max = defaultNumEntries
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.head = (s.head + 1) % s.max
 | 
			
		||||
	s.entries[s.head] = a
 | 
			
		||||
	if s.size < s.max {
 | 
			
		||||
		s.size++
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NthPreviousEntry returns the value passed to the nth previous call to Add.
 | 
			
		||||
// If n is zero then the immediately prior value is returned, if one, then the
 | 
			
		||||
// next most recent, and so on. If such an element doesn't exist then ok is
 | 
			
		||||
// false.
 | 
			
		||||
func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
 | 
			
		||||
	if n >= s.size {
 | 
			
		||||
		return "", false
 | 
			
		||||
	}
 | 
			
		||||
	index := s.head - n
 | 
			
		||||
	if index < 0 {
 | 
			
		||||
		index += s.max
 | 
			
		||||
	}
 | 
			
		||||
	return s.entries[index], true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readPasswordLine reads from reader until it finds \n or io.EOF.
 | 
			
		||||
// The slice returned does not include the \n.
 | 
			
		||||
// readPasswordLine also ignores any \r it finds.
 | 
			
		||||
func readPasswordLine(reader io.Reader) ([]byte, error) {
 | 
			
		||||
	var buf [1]byte
 | 
			
		||||
	var ret []byte
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		n, err := reader.Read(buf[:])
 | 
			
		||||
		if n > 0 {
 | 
			
		||||
			switch buf[0] {
 | 
			
		||||
			case '\n':
 | 
			
		||||
				return ret, nil
 | 
			
		||||
			case '\r':
 | 
			
		||||
				// remove \r from passwords on Windows
 | 
			
		||||
			default:
 | 
			
		||||
				ret = append(ret, buf[0])
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err == io.EOF && len(ret) > 0 {
 | 
			
		||||
				return ret, nil
 | 
			
		||||
			}
 | 
			
		||||
			return ret, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										116
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd
 | 
			
		||||
 | 
			
		||||
// Package terminal provides support functions for dealing with terminals, as
 | 
			
		||||
// commonly found on UNIX systems.
 | 
			
		||||
//
 | 
			
		||||
// Putting a terminal into raw mode is the most common requirement:
 | 
			
		||||
//
 | 
			
		||||
// 	oldState, err := terminal.MakeRaw(0)
 | 
			
		||||
// 	if err != nil {
 | 
			
		||||
// 	        panic(err)
 | 
			
		||||
// 	}
 | 
			
		||||
// 	defer terminal.Restore(0, oldState)
 | 
			
		||||
package terminal // import "golang.org/x/crypto/ssh/terminal"
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"golang.org/x/sys/unix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// State contains the state of a terminal.
 | 
			
		||||
type State struct {
 | 
			
		||||
	termios unix.Termios
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsTerminal returns true if the given file descriptor is a terminal.
 | 
			
		||||
func IsTerminal(fd int) bool {
 | 
			
		||||
	_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
 | 
			
		||||
	return err == nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MakeRaw put the terminal connected to the given file descriptor into raw
 | 
			
		||||
// mode and returns the previous state of the terminal so that it can be
 | 
			
		||||
// restored.
 | 
			
		||||
func MakeRaw(fd int) (*State, error) {
 | 
			
		||||
	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	oldState := State{termios: *termios}
 | 
			
		||||
 | 
			
		||||
	// This attempts to replicate the behaviour documented for cfmakeraw in
 | 
			
		||||
	// the termios(3) manpage.
 | 
			
		||||
	termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
 | 
			
		||||
	termios.Oflag &^= unix.OPOST
 | 
			
		||||
	termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
 | 
			
		||||
	termios.Cflag &^= unix.CSIZE | unix.PARENB
 | 
			
		||||
	termios.Cflag |= unix.CS8
 | 
			
		||||
	termios.Cc[unix.VMIN] = 1
 | 
			
		||||
	termios.Cc[unix.VTIME] = 0
 | 
			
		||||
	if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &oldState, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetState returns the current state of a terminal which may be useful to
 | 
			
		||||
// restore the terminal after a signal.
 | 
			
		||||
func GetState(fd int) (*State, error) {
 | 
			
		||||
	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &State{termios: *termios}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Restore restores the terminal connected to the given file descriptor to a
 | 
			
		||||
// previous state.
 | 
			
		||||
func Restore(fd int, state *State) error {
 | 
			
		||||
	return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSize returns the dimensions of the given terminal.
 | 
			
		||||
func GetSize(fd int) (width, height int, err error) {
 | 
			
		||||
	ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return -1, -1, err
 | 
			
		||||
	}
 | 
			
		||||
	return int(ws.Col), int(ws.Row), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// passwordReader is an io.Reader that reads from a specific file descriptor.
 | 
			
		||||
type passwordReader int
 | 
			
		||||
 | 
			
		||||
func (r passwordReader) Read(buf []byte) (int, error) {
 | 
			
		||||
	return unix.Read(int(r), buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadPassword reads a line of input from a terminal without local echo.  This
 | 
			
		||||
// is commonly used for inputting passwords and other sensitive data. The slice
 | 
			
		||||
// returned does not include the \n.
 | 
			
		||||
func ReadPassword(fd int) ([]byte, error) {
 | 
			
		||||
	termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newState := *termios
 | 
			
		||||
	newState.Lflag &^= unix.ECHO
 | 
			
		||||
	newState.Lflag |= unix.ICANON | unix.ISIG
 | 
			
		||||
	newState.Iflag |= unix.ICRNL
 | 
			
		||||
	if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return readPasswordLine(passwordReader(fd))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
// Copyright 2013 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build darwin dragonfly freebsd netbsd openbsd
 | 
			
		||||
 | 
			
		||||
package terminal
 | 
			
		||||
 | 
			
		||||
import "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
const ioctlReadTermios = unix.TIOCGETA
 | 
			
		||||
const ioctlWriteTermios = unix.TIOCSETA
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
// Copyright 2013 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package terminal
 | 
			
		||||
 | 
			
		||||
import "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
const ioctlReadTermios = unix.TCGETS
 | 
			
		||||
const ioctlWriteTermios = unix.TCSETS
 | 
			
		||||
							
								
								
									
										58
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Package terminal provides support functions for dealing with terminals, as
 | 
			
		||||
// commonly found on UNIX systems.
 | 
			
		||||
//
 | 
			
		||||
// Putting a terminal into raw mode is the most common requirement:
 | 
			
		||||
//
 | 
			
		||||
// 	oldState, err := terminal.MakeRaw(0)
 | 
			
		||||
// 	if err != nil {
 | 
			
		||||
// 	        panic(err)
 | 
			
		||||
// 	}
 | 
			
		||||
// 	defer terminal.Restore(0, oldState)
 | 
			
		||||
package terminal
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type State struct{}
 | 
			
		||||
 | 
			
		||||
// IsTerminal returns true if the given file descriptor is a terminal.
 | 
			
		||||
func IsTerminal(fd int) bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MakeRaw put the terminal connected to the given file descriptor into raw
 | 
			
		||||
// mode and returns the previous state of the terminal so that it can be
 | 
			
		||||
// restored.
 | 
			
		||||
func MakeRaw(fd int) (*State, error) {
 | 
			
		||||
	return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetState returns the current state of a terminal which may be useful to
 | 
			
		||||
// restore the terminal after a signal.
 | 
			
		||||
func GetState(fd int) (*State, error) {
 | 
			
		||||
	return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Restore restores the terminal connected to the given file descriptor to a
 | 
			
		||||
// previous state.
 | 
			
		||||
func Restore(fd int, state *State) error {
 | 
			
		||||
	return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSize returns the dimensions of the given terminal.
 | 
			
		||||
func GetSize(fd int) (width, height int, err error) {
 | 
			
		||||
	return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadPassword reads a line of input from a terminal without local echo.  This
 | 
			
		||||
// is commonly used for inputting passwords and other sensitive data. The slice
 | 
			
		||||
// returned does not include the \n.
 | 
			
		||||
func ReadPassword(fd int) ([]byte, error) {
 | 
			
		||||
	return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										128
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
			
		||||
// Copyright 2015 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build solaris
 | 
			
		||||
 | 
			
		||||
package terminal // import "golang.org/x/crypto/ssh/terminal"
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"golang.org/x/sys/unix"
 | 
			
		||||
	"io"
 | 
			
		||||
	"syscall"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// State contains the state of a terminal.
 | 
			
		||||
type State struct {
 | 
			
		||||
	state *unix.Termios
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsTerminal returns true if the given file descriptor is a terminal.
 | 
			
		||||
func IsTerminal(fd int) bool {
 | 
			
		||||
	_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
 | 
			
		||||
	return err == nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadPassword reads a line of input from a terminal without local echo.  This
 | 
			
		||||
// is commonly used for inputting passwords and other sensitive data. The slice
 | 
			
		||||
// returned does not include the \n.
 | 
			
		||||
func ReadPassword(fd int) ([]byte, error) {
 | 
			
		||||
	// see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
 | 
			
		||||
	val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	oldState := *val
 | 
			
		||||
 | 
			
		||||
	newState := oldState
 | 
			
		||||
	newState.Lflag &^= syscall.ECHO
 | 
			
		||||
	newState.Lflag |= syscall.ICANON | syscall.ISIG
 | 
			
		||||
	newState.Iflag |= syscall.ICRNL
 | 
			
		||||
	err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)
 | 
			
		||||
 | 
			
		||||
	var buf [16]byte
 | 
			
		||||
	var ret []byte
 | 
			
		||||
	for {
 | 
			
		||||
		n, err := syscall.Read(fd, buf[:])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if n == 0 {
 | 
			
		||||
			if len(ret) == 0 {
 | 
			
		||||
				return nil, io.EOF
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if buf[n-1] == '\n' {
 | 
			
		||||
			n--
 | 
			
		||||
		}
 | 
			
		||||
		ret = append(ret, buf[:n]...)
 | 
			
		||||
		if n < len(buf) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MakeRaw puts the terminal connected to the given file descriptor into raw
 | 
			
		||||
// mode and returns the previous state of the terminal so that it can be
 | 
			
		||||
// restored.
 | 
			
		||||
// see http://cr.illumos.org/~webrev/andy_js/1060/
 | 
			
		||||
func MakeRaw(fd int) (*State, error) {
 | 
			
		||||
	oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	oldTermios := *oldTermiosPtr
 | 
			
		||||
 | 
			
		||||
	newTermios := oldTermios
 | 
			
		||||
	newTermios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
 | 
			
		||||
	newTermios.Oflag &^= syscall.OPOST
 | 
			
		||||
	newTermios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
 | 
			
		||||
	newTermios.Cflag &^= syscall.CSIZE | syscall.PARENB
 | 
			
		||||
	newTermios.Cflag |= syscall.CS8
 | 
			
		||||
	newTermios.Cc[unix.VMIN] = 1
 | 
			
		||||
	newTermios.Cc[unix.VTIME] = 0
 | 
			
		||||
 | 
			
		||||
	if err := unix.IoctlSetTermios(fd, unix.TCSETS, &newTermios); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &State{
 | 
			
		||||
		state: oldTermiosPtr,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Restore restores the terminal connected to the given file descriptor to a
 | 
			
		||||
// previous state.
 | 
			
		||||
func Restore(fd int, oldState *State) error {
 | 
			
		||||
	return unix.IoctlSetTermios(fd, unix.TCSETS, oldState.state)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetState returns the current state of a terminal which may be useful to
 | 
			
		||||
// restore the terminal after a signal.
 | 
			
		||||
func GetState(fd int) (*State, error) {
 | 
			
		||||
	oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &State{
 | 
			
		||||
		state: oldTermiosPtr,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSize returns the dimensions of the given terminal.
 | 
			
		||||
func GetSize(fd int) (width, height int, err error) {
 | 
			
		||||
	ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return int(ws.Col), int(ws.Row), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										97
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								vendor/golang.org/x/crypto/ssh/terminal/util_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build windows
 | 
			
		||||
 | 
			
		||||
// Package terminal provides support functions for dealing with terminals, as
 | 
			
		||||
// commonly found on UNIX systems.
 | 
			
		||||
//
 | 
			
		||||
// Putting a terminal into raw mode is the most common requirement:
 | 
			
		||||
//
 | 
			
		||||
// 	oldState, err := terminal.MakeRaw(0)
 | 
			
		||||
// 	if err != nil {
 | 
			
		||||
// 	        panic(err)
 | 
			
		||||
// 	}
 | 
			
		||||
// 	defer terminal.Restore(0, oldState)
 | 
			
		||||
package terminal
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/sys/windows"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type State struct {
 | 
			
		||||
	mode uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsTerminal returns true if the given file descriptor is a terminal.
 | 
			
		||||
func IsTerminal(fd int) bool {
 | 
			
		||||
	var st uint32
 | 
			
		||||
	err := windows.GetConsoleMode(windows.Handle(fd), &st)
 | 
			
		||||
	return err == nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MakeRaw put the terminal connected to the given file descriptor into raw
 | 
			
		||||
// mode and returns the previous state of the terminal so that it can be
 | 
			
		||||
// restored.
 | 
			
		||||
func MakeRaw(fd int) (*State, error) {
 | 
			
		||||
	var st uint32
 | 
			
		||||
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
 | 
			
		||||
	if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &State{st}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetState returns the current state of a terminal which may be useful to
 | 
			
		||||
// restore the terminal after a signal.
 | 
			
		||||
func GetState(fd int) (*State, error) {
 | 
			
		||||
	var st uint32
 | 
			
		||||
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &State{st}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Restore restores the terminal connected to the given file descriptor to a
 | 
			
		||||
// previous state.
 | 
			
		||||
func Restore(fd int, state *State) error {
 | 
			
		||||
	return windows.SetConsoleMode(windows.Handle(fd), state.mode)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSize returns the dimensions of the given terminal.
 | 
			
		||||
func GetSize(fd int) (width, height int, err error) {
 | 
			
		||||
	var info windows.ConsoleScreenBufferInfo
 | 
			
		||||
	if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
 | 
			
		||||
		return 0, 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return int(info.Size.X), int(info.Size.Y), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadPassword reads a line of input from a terminal without local echo.  This
 | 
			
		||||
// is commonly used for inputting passwords and other sensitive data. The slice
 | 
			
		||||
// returned does not include the \n.
 | 
			
		||||
func ReadPassword(fd int) ([]byte, error) {
 | 
			
		||||
	var st uint32
 | 
			
		||||
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	old := st
 | 
			
		||||
 | 
			
		||||
	st &^= (windows.ENABLE_ECHO_INPUT)
 | 
			
		||||
	st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
 | 
			
		||||
	if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		windows.SetConsoleMode(windows.Handle(fd), old)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return readPasswordLine(os.NewFile(uintptr(fd), "stdin"))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								vendor/golang.org/x/crypto/ssh/test/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								vendor/golang.org/x/crypto/ssh/test/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Package test contains integration tests for the
 | 
			
		||||
// golang.org/x/crypto/ssh package.
 | 
			
		||||
package test // import "golang.org/x/crypto/ssh/test"
 | 
			
		||||
							
								
								
									
										375
									
								
								vendor/golang.org/x/crypto/ssh/transport.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										375
									
								
								vendor/golang.org/x/crypto/ssh/transport.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,375 @@
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package ssh
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// debugTransport if set, will print packet types as they go over the
 | 
			
		||||
// wire. No message decoding is done, to minimize the impact on timing.
 | 
			
		||||
const debugTransport = false
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	gcmCipherID    = "aes128-gcm@openssh.com"
 | 
			
		||||
	aes128cbcID    = "aes128-cbc"
 | 
			
		||||
	tripledescbcID = "3des-cbc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// packetConn represents a transport that implements packet based
 | 
			
		||||
// operations.
 | 
			
		||||
type packetConn interface {
 | 
			
		||||
	// Encrypt and send a packet of data to the remote peer.
 | 
			
		||||
	writePacket(packet []byte) error
 | 
			
		||||
 | 
			
		||||
	// Read a packet from the connection. The read is blocking,
 | 
			
		||||
	// i.e. if error is nil, then the returned byte slice is
 | 
			
		||||
	// always non-empty.
 | 
			
		||||
	readPacket() ([]byte, error)
 | 
			
		||||
 | 
			
		||||
	// Close closes the write-side of the connection.
 | 
			
		||||
	Close() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// transport is the keyingTransport that implements the SSH packet
 | 
			
		||||
// protocol.
 | 
			
		||||
type transport struct {
 | 
			
		||||
	reader connectionState
 | 
			
		||||
	writer connectionState
 | 
			
		||||
 | 
			
		||||
	bufReader *bufio.Reader
 | 
			
		||||
	bufWriter *bufio.Writer
 | 
			
		||||
	rand      io.Reader
 | 
			
		||||
	isClient  bool
 | 
			
		||||
	io.Closer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// packetCipher represents a combination of SSH encryption/MAC
 | 
			
		||||
// protocol.  A single instance should be used for one direction only.
 | 
			
		||||
type packetCipher interface {
 | 
			
		||||
	// writePacket encrypts the packet and writes it to w. The
 | 
			
		||||
	// contents of the packet are generally scrambled.
 | 
			
		||||
	writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error
 | 
			
		||||
 | 
			
		||||
	// readPacket reads and decrypts a packet of data. The
 | 
			
		||||
	// returned packet may be overwritten by future calls of
 | 
			
		||||
	// readPacket.
 | 
			
		||||
	readPacket(seqnum uint32, r io.Reader) ([]byte, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// connectionState represents one side (read or write) of the
 | 
			
		||||
// connection. This is necessary because each direction has its own
 | 
			
		||||
// keys, and can even have its own algorithms
 | 
			
		||||
type connectionState struct {
 | 
			
		||||
	packetCipher
 | 
			
		||||
	seqNum           uint32
 | 
			
		||||
	dir              direction
 | 
			
		||||
	pendingKeyChange chan packetCipher
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// prepareKeyChange sets up key material for a keychange. The key changes in
 | 
			
		||||
// both directions are triggered by reading and writing a msgNewKey packet
 | 
			
		||||
// respectively.
 | 
			
		||||
func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
 | 
			
		||||
	ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	t.reader.pendingKeyChange <- ciph
 | 
			
		||||
 | 
			
		||||
	ciph, err = newPacketCipher(t.writer.dir, algs.w, kexResult)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	t.writer.pendingKeyChange <- ciph
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *transport) printPacket(p []byte, write bool) {
 | 
			
		||||
	if len(p) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	who := "server"
 | 
			
		||||
	if t.isClient {
 | 
			
		||||
		who = "client"
 | 
			
		||||
	}
 | 
			
		||||
	what := "read"
 | 
			
		||||
	if write {
 | 
			
		||||
		what = "write"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Println(what, who, p[0])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read and decrypt next packet.
 | 
			
		||||
func (t *transport) readPacket() (p []byte, err error) {
 | 
			
		||||
	for {
 | 
			
		||||
		p, err = t.reader.readPacket(t.bufReader)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if debugTransport {
 | 
			
		||||
		t.printPacket(p, false)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
 | 
			
		||||
	packet, err := s.packetCipher.readPacket(s.seqNum, r)
 | 
			
		||||
	s.seqNum++
 | 
			
		||||
	if err == nil && len(packet) == 0 {
 | 
			
		||||
		err = errors.New("ssh: zero length packet")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(packet) > 0 {
 | 
			
		||||
		switch packet[0] {
 | 
			
		||||
		case msgNewKeys:
 | 
			
		||||
			select {
 | 
			
		||||
			case cipher := <-s.pendingKeyChange:
 | 
			
		||||
				s.packetCipher = cipher
 | 
			
		||||
			default:
 | 
			
		||||
				return nil, errors.New("ssh: got bogus newkeys message")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case msgDisconnect:
 | 
			
		||||
			// Transform a disconnect message into an
 | 
			
		||||
			// error. Since this is lowest level at which
 | 
			
		||||
			// we interpret message types, doing it here
 | 
			
		||||
			// ensures that we don't have to handle it
 | 
			
		||||
			// elsewhere.
 | 
			
		||||
			var msg disconnectMsg
 | 
			
		||||
			if err := Unmarshal(packet, &msg); err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			return nil, &msg
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The packet may point to an internal buffer, so copy the
 | 
			
		||||
	// packet out here.
 | 
			
		||||
	fresh := make([]byte, len(packet))
 | 
			
		||||
	copy(fresh, packet)
 | 
			
		||||
 | 
			
		||||
	return fresh, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *transport) writePacket(packet []byte) error {
 | 
			
		||||
	if debugTransport {
 | 
			
		||||
		t.printPacket(packet, true)
 | 
			
		||||
	}
 | 
			
		||||
	return t.writer.writePacket(t.bufWriter, t.rand, packet)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error {
 | 
			
		||||
	changeKeys := len(packet) > 0 && packet[0] == msgNewKeys
 | 
			
		||||
 | 
			
		||||
	err := s.packetCipher.writePacket(s.seqNum, w, rand, packet)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err = w.Flush(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	s.seqNum++
 | 
			
		||||
	if changeKeys {
 | 
			
		||||
		select {
 | 
			
		||||
		case cipher := <-s.pendingKeyChange:
 | 
			
		||||
			s.packetCipher = cipher
 | 
			
		||||
		default:
 | 
			
		||||
			panic("ssh: no key material for msgNewKeys")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport {
 | 
			
		||||
	t := &transport{
 | 
			
		||||
		bufReader: bufio.NewReader(rwc),
 | 
			
		||||
		bufWriter: bufio.NewWriter(rwc),
 | 
			
		||||
		rand:      rand,
 | 
			
		||||
		reader: connectionState{
 | 
			
		||||
			packetCipher:     &streamPacketCipher{cipher: noneCipher{}},
 | 
			
		||||
			pendingKeyChange: make(chan packetCipher, 1),
 | 
			
		||||
		},
 | 
			
		||||
		writer: connectionState{
 | 
			
		||||
			packetCipher:     &streamPacketCipher{cipher: noneCipher{}},
 | 
			
		||||
			pendingKeyChange: make(chan packetCipher, 1),
 | 
			
		||||
		},
 | 
			
		||||
		Closer: rwc,
 | 
			
		||||
	}
 | 
			
		||||
	t.isClient = isClient
 | 
			
		||||
 | 
			
		||||
	if isClient {
 | 
			
		||||
		t.reader.dir = serverKeys
 | 
			
		||||
		t.writer.dir = clientKeys
 | 
			
		||||
	} else {
 | 
			
		||||
		t.reader.dir = clientKeys
 | 
			
		||||
		t.writer.dir = serverKeys
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type direction struct {
 | 
			
		||||
	ivTag     []byte
 | 
			
		||||
	keyTag    []byte
 | 
			
		||||
	macKeyTag []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}}
 | 
			
		||||
	clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// generateKeys generates key material for IV, MAC and encryption.
 | 
			
		||||
func generateKeys(d direction, algs directionAlgorithms, kex *kexResult) (iv, key, macKey []byte) {
 | 
			
		||||
	cipherMode := cipherModes[algs.Cipher]
 | 
			
		||||
	macMode := macModes[algs.MAC]
 | 
			
		||||
 | 
			
		||||
	iv = make([]byte, cipherMode.ivSize)
 | 
			
		||||
	key = make([]byte, cipherMode.keySize)
 | 
			
		||||
	macKey = make([]byte, macMode.keySize)
 | 
			
		||||
 | 
			
		||||
	generateKeyMaterial(iv, d.ivTag, kex)
 | 
			
		||||
	generateKeyMaterial(key, d.keyTag, kex)
 | 
			
		||||
	generateKeyMaterial(macKey, d.macKeyTag, kex)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as
 | 
			
		||||
// described in RFC 4253, section 6.4. direction should either be serverKeys
 | 
			
		||||
// (to setup server->client keys) or clientKeys (for client->server keys).
 | 
			
		||||
func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) {
 | 
			
		||||
	iv, key, macKey := generateKeys(d, algs, kex)
 | 
			
		||||
 | 
			
		||||
	if algs.Cipher == gcmCipherID {
 | 
			
		||||
		return newGCMCipher(iv, key)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if algs.Cipher == aes128cbcID {
 | 
			
		||||
		return newAESCBCCipher(iv, key, macKey, algs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if algs.Cipher == tripledescbcID {
 | 
			
		||||
		return newTripleDESCBCCipher(iv, key, macKey, algs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := &streamPacketCipher{
 | 
			
		||||
		mac: macModes[algs.MAC].new(macKey),
 | 
			
		||||
		etm: macModes[algs.MAC].etm,
 | 
			
		||||
	}
 | 
			
		||||
	c.macResult = make([]byte, c.mac.Size())
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// generateKeyMaterial fills out with key material generated from tag, K, H
 | 
			
		||||
// and sessionId, as specified in RFC 4253, section 7.2.
 | 
			
		||||
func generateKeyMaterial(out, tag []byte, r *kexResult) {
 | 
			
		||||
	var digestsSoFar []byte
 | 
			
		||||
 | 
			
		||||
	h := r.Hash.New()
 | 
			
		||||
	for len(out) > 0 {
 | 
			
		||||
		h.Reset()
 | 
			
		||||
		h.Write(r.K)
 | 
			
		||||
		h.Write(r.H)
 | 
			
		||||
 | 
			
		||||
		if len(digestsSoFar) == 0 {
 | 
			
		||||
			h.Write(tag)
 | 
			
		||||
			h.Write(r.SessionID)
 | 
			
		||||
		} else {
 | 
			
		||||
			h.Write(digestsSoFar)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		digest := h.Sum(nil)
 | 
			
		||||
		n := copy(out, digest)
 | 
			
		||||
		out = out[n:]
 | 
			
		||||
		if len(out) > 0 {
 | 
			
		||||
			digestsSoFar = append(digestsSoFar, digest...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const packageVersion = "SSH-2.0-Go"
 | 
			
		||||
 | 
			
		||||
// Sends and receives a version line.  The versionLine string should
 | 
			
		||||
// be US ASCII, start with "SSH-2.0-", and should not include a
 | 
			
		||||
// newline. exchangeVersions returns the other side's version line.
 | 
			
		||||
func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) {
 | 
			
		||||
	// Contrary to the RFC, we do not ignore lines that don't
 | 
			
		||||
	// start with "SSH-2.0-" to make the library usable with
 | 
			
		||||
	// nonconforming servers.
 | 
			
		||||
	for _, c := range versionLine {
 | 
			
		||||
		// The spec disallows non US-ASCII chars, and
 | 
			
		||||
		// specifically forbids null chars.
 | 
			
		||||
		if c < 32 {
 | 
			
		||||
			return nil, errors.New("ssh: junk character in version line")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	them, err = readVersion(rw)
 | 
			
		||||
	return them, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// maxVersionStringBytes is the maximum number of bytes that we'll
 | 
			
		||||
// accept as a version string. RFC 4253 section 4.2 limits this at 255
 | 
			
		||||
// chars
 | 
			
		||||
const maxVersionStringBytes = 255
 | 
			
		||||
 | 
			
		||||
// Read version string as specified by RFC 4253, section 4.2.
 | 
			
		||||
func readVersion(r io.Reader) ([]byte, error) {
 | 
			
		||||
	versionString := make([]byte, 0, 64)
 | 
			
		||||
	var ok bool
 | 
			
		||||
	var buf [1]byte
 | 
			
		||||
 | 
			
		||||
	for len(versionString) < maxVersionStringBytes {
 | 
			
		||||
		_, err := io.ReadFull(r, buf[:])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		// The RFC says that the version should be terminated with \r\n
 | 
			
		||||
		// but several SSH servers actually only send a \n.
 | 
			
		||||
		if buf[0] == '\n' {
 | 
			
		||||
			ok = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// non ASCII chars are disallowed, but we are lenient,
 | 
			
		||||
		// since Go doesn't use null-terminated strings.
 | 
			
		||||
 | 
			
		||||
		// The RFC allows a comment after a space, however,
 | 
			
		||||
		// all of it (version and comments) goes into the
 | 
			
		||||
		// session hash.
 | 
			
		||||
		versionString = append(versionString, buf[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("ssh: overflow reading version string")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// There might be a '\r' on the end which we should remove.
 | 
			
		||||
	if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' {
 | 
			
		||||
		versionString = versionString[:len(versionString)-1]
 | 
			
		||||
	}
 | 
			
		||||
	return versionString, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								vendor/golang.org/x/sys/unix/asm_openbsd_arm.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/golang.org/x/sys/unix/asm_openbsd_arm.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
// Copyright 2017 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for ARM, OpenBSD
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	B	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	B	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-52
 | 
			
		||||
	B	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	B	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	B	syscall·RawSyscall6(SB)
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/golang.org/x/sys/unix/asm_solaris_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/golang.org/x/sys/unix/asm_solaris_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -10,8 +10,8 @@
 | 
			
		||||
// System calls for amd64, Solaris are implemented in runtime/syscall_solaris.go
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
TEXT ·sysvicall6(SB),NOSPLIT,$0-64
 | 
			
		||||
TEXT ·sysvicall6(SB),NOSPLIT,$0-88
 | 
			
		||||
	JMP	syscall·sysvicall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·rawSysvicall6(SB),NOSPLIT,$0-64
 | 
			
		||||
TEXT ·rawSysvicall6(SB),NOSPLIT,$0-88
 | 
			
		||||
	JMP	syscall·rawSysvicall6(SB)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										195
									
								
								vendor/golang.org/x/sys/unix/cap_freebsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								vendor/golang.org/x/sys/unix/cap_freebsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,195 @@
 | 
			
		||||
// Copyright 2017 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build freebsd
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	errorspkg "errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Go implementation of C mostly found in /usr/src/sys/kern/subr_capability.c
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// This is the version of CapRights this package understands. See C implementation for parallels.
 | 
			
		||||
	capRightsGoVersion = CAP_RIGHTS_VERSION_00
 | 
			
		||||
	capArSizeMin       = CAP_RIGHTS_VERSION_00 + 2
 | 
			
		||||
	capArSizeMax       = capRightsGoVersion + 2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	bit2idx = []int{
 | 
			
		||||
		-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1,
 | 
			
		||||
		4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func capidxbit(right uint64) int {
 | 
			
		||||
	return int((right >> 57) & 0x1f)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func rightToIndex(right uint64) (int, error) {
 | 
			
		||||
	idx := capidxbit(right)
 | 
			
		||||
	if idx < 0 || idx >= len(bit2idx) {
 | 
			
		||||
		return -2, fmt.Errorf("index for right 0x%x out of range", right)
 | 
			
		||||
	}
 | 
			
		||||
	return bit2idx[idx], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func caprver(right uint64) int {
 | 
			
		||||
	return int(right >> 62)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func capver(rights *CapRights) int {
 | 
			
		||||
	return caprver(rights.Rights[0])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func caparsize(rights *CapRights) int {
 | 
			
		||||
	return capver(rights) + 2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CapRightsSet sets the permissions in setrights in rights.
 | 
			
		||||
func CapRightsSet(rights *CapRights, setrights []uint64) error {
 | 
			
		||||
	// This is essentially a copy of cap_rights_vset()
 | 
			
		||||
	if capver(rights) != CAP_RIGHTS_VERSION_00 {
 | 
			
		||||
		return fmt.Errorf("bad rights version %d", capver(rights))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	n := caparsize(rights)
 | 
			
		||||
	if n < capArSizeMin || n > capArSizeMax {
 | 
			
		||||
		return errorspkg.New("bad rights size")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, right := range setrights {
 | 
			
		||||
		if caprver(right) != CAP_RIGHTS_VERSION_00 {
 | 
			
		||||
			return errorspkg.New("bad right version")
 | 
			
		||||
		}
 | 
			
		||||
		i, err := rightToIndex(right)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if i >= n {
 | 
			
		||||
			return errorspkg.New("index overflow")
 | 
			
		||||
		}
 | 
			
		||||
		if capidxbit(rights.Rights[i]) != capidxbit(right) {
 | 
			
		||||
			return errorspkg.New("index mismatch")
 | 
			
		||||
		}
 | 
			
		||||
		rights.Rights[i] |= right
 | 
			
		||||
		if capidxbit(rights.Rights[i]) != capidxbit(right) {
 | 
			
		||||
			return errorspkg.New("index mismatch (after assign)")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CapRightsClear clears the permissions in clearrights from rights.
 | 
			
		||||
func CapRightsClear(rights *CapRights, clearrights []uint64) error {
 | 
			
		||||
	// This is essentially a copy of cap_rights_vclear()
 | 
			
		||||
	if capver(rights) != CAP_RIGHTS_VERSION_00 {
 | 
			
		||||
		return fmt.Errorf("bad rights version %d", capver(rights))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	n := caparsize(rights)
 | 
			
		||||
	if n < capArSizeMin || n > capArSizeMax {
 | 
			
		||||
		return errorspkg.New("bad rights size")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, right := range clearrights {
 | 
			
		||||
		if caprver(right) != CAP_RIGHTS_VERSION_00 {
 | 
			
		||||
			return errorspkg.New("bad right version")
 | 
			
		||||
		}
 | 
			
		||||
		i, err := rightToIndex(right)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if i >= n {
 | 
			
		||||
			return errorspkg.New("index overflow")
 | 
			
		||||
		}
 | 
			
		||||
		if capidxbit(rights.Rights[i]) != capidxbit(right) {
 | 
			
		||||
			return errorspkg.New("index mismatch")
 | 
			
		||||
		}
 | 
			
		||||
		rights.Rights[i] &= ^(right & 0x01FFFFFFFFFFFFFF)
 | 
			
		||||
		if capidxbit(rights.Rights[i]) != capidxbit(right) {
 | 
			
		||||
			return errorspkg.New("index mismatch (after assign)")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CapRightsIsSet checks whether all the permissions in setrights are present in rights.
 | 
			
		||||
func CapRightsIsSet(rights *CapRights, setrights []uint64) (bool, error) {
 | 
			
		||||
	// This is essentially a copy of cap_rights_is_vset()
 | 
			
		||||
	if capver(rights) != CAP_RIGHTS_VERSION_00 {
 | 
			
		||||
		return false, fmt.Errorf("bad rights version %d", capver(rights))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	n := caparsize(rights)
 | 
			
		||||
	if n < capArSizeMin || n > capArSizeMax {
 | 
			
		||||
		return false, errorspkg.New("bad rights size")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, right := range setrights {
 | 
			
		||||
		if caprver(right) != CAP_RIGHTS_VERSION_00 {
 | 
			
		||||
			return false, errorspkg.New("bad right version")
 | 
			
		||||
		}
 | 
			
		||||
		i, err := rightToIndex(right)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
		if i >= n {
 | 
			
		||||
			return false, errorspkg.New("index overflow")
 | 
			
		||||
		}
 | 
			
		||||
		if capidxbit(rights.Rights[i]) != capidxbit(right) {
 | 
			
		||||
			return false, errorspkg.New("index mismatch")
 | 
			
		||||
		}
 | 
			
		||||
		if (rights.Rights[i] & right) != right {
 | 
			
		||||
			return false, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func capright(idx uint64, bit uint64) uint64 {
 | 
			
		||||
	return ((1 << (57 + idx)) | bit)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CapRightsInit returns a pointer to an initialised CapRights structure filled with rights.
 | 
			
		||||
// See man cap_rights_init(3) and rights(4).
 | 
			
		||||
func CapRightsInit(rights []uint64) (*CapRights, error) {
 | 
			
		||||
	var r CapRights
 | 
			
		||||
	r.Rights[0] = (capRightsGoVersion << 62) | capright(0, 0)
 | 
			
		||||
	r.Rights[1] = capright(1, 0)
 | 
			
		||||
 | 
			
		||||
	err := CapRightsSet(&r, rights)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CapRightsLimit reduces the operations permitted on fd to at most those contained in rights.
 | 
			
		||||
// The capability rights on fd can never be increased by CapRightsLimit.
 | 
			
		||||
// See man cap_rights_limit(2) and rights(4).
 | 
			
		||||
func CapRightsLimit(fd uintptr, rights *CapRights) error {
 | 
			
		||||
	return capRightsLimit(int(fd), rights)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CapRightsGet returns a CapRights structure containing the operations permitted on fd.
 | 
			
		||||
// See man cap_rights_get(3) and rights(4).
 | 
			
		||||
func CapRightsGet(fd uintptr) (*CapRights, error) {
 | 
			
		||||
	r, err := CapRightsInit(nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	err = capRightsGet(capRightsGoVersion, int(fd), r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user