Update dependencies

This commit is contained in:
Zack Scholl 2017-10-20 06:29:52 -06:00
parent c3cd3d6039
commit d663ff861b
17 changed files with 655 additions and 14 deletions

12
Gopkg.lock generated
View File

@ -13,6 +13,12 @@
packages = [".","util/strutil"]
revision = "d0567a9d84a1c40dd7568115ea66f4887bf57b33"
[[projects]]
branch = "master"
name = "github.com/mars9/crypt"
packages = ["."]
revision = "65899cf653ff022fe5c7fe504b439feed9e7e0fc"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
@ -40,8 +46,8 @@
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
revision = "9419663f5a44be8b34ca85f08abc5fe1be11f8a3"
packages = ["pbkdf2","scrypt","ssh/terminal"]
revision = "541b9d50ad47e36efd8fb423e938e59ff1691f68"
[[projects]]
branch = "master"
@ -58,6 +64,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "b5f57442f43856cf17500cec2a357c4ea34e5f9eae4298954f62765111b0d5a9"
inputs-digest = "b7969c854fd8997ea62c1a9ed968e5eeac091a3fc5650cca64e99e888a0156db"
solver-name = "gps-cdcl"
solver-version = 1

1
vendor/github.com/mars9/crypt/.gitignore generated vendored Normal file
View File

@ -0,0 +1 @@
crypt/crypt

13
vendor/github.com/mars9/crypt/LICENSE generated vendored Normal file
View File

@ -0,0 +1,13 @@
Copyright (c) 2013 Markus Sonderegger
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

4
vendor/github.com/mars9/crypt/README.md generated vendored Normal file
View File

@ -0,0 +1,4 @@
# crypt
Package crypt provides password-based encryption and decryption of
data streams.

165
vendor/github.com/mars9/crypt/crypt.go generated vendored Normal file
View File

@ -0,0 +1,165 @@
// Package crypt provides password-based encryption and decryption of
// data streams.
package crypt
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"errors"
"hash"
"io"
)
const (
blockSize = aes.BlockSize // AES block size
version = 1
)
// Crypter encrypt/decrypts with AES (Rijndael) in cipher block counter
// mode (CTR) and authenticate with HMAC-SHA.
type Crypter struct {
HashFunc func() hash.Hash
HashSize int
Key Key
BufSize int
}
func (c *Crypter) encHeader(salt, iv, hmacKey []byte) []byte {
keySize := c.Key.Size()
headerSize := 1 + keySize + blockSize + c.HashSize
b := make([]byte, headerSize)
b[0] = version
copy(b[1:1+keySize], salt)
copy(b[1+keySize:1+keySize+blockSize], iv)
mac := hmac.New(c.HashFunc, hmacKey)
mac.Write(b[:1+keySize+blockSize])
copy(b[1+keySize+blockSize:], mac.Sum(nil))
return b
}
func (c *Crypter) bufSize() int {
if c.BufSize == 0 {
return 2 * 1024 * 1024
}
return c.BufSize
}
func (c *Crypter) decHeader(b []byte) ([]byte, []byte, error) {
if b[0] != version {
return nil, nil, errors.New("malformed encrypted packet")
}
keySize := c.Key.Size()
salt := b[1 : 1+keySize]
iv := b[1+keySize : 1+keySize+blockSize]
return salt, iv, nil
}
// Encrypt encrypts from src until either EOF is reached on src or an
// error occurs. A successful Encrypt returns err == nil, not err == EOF.
func (c *Crypter) Encrypt(dst io.Writer, src io.Reader) (err error) {
salt := make([]byte, c.Key.Size())
if _, err := rand.Read(salt); err != nil {
return err
}
iv := make([]byte, blockSize)
if _, err := rand.Read(iv); err != nil {
return err
}
aesKey, hmacKey := c.Key.Derive(salt)
header := c.encHeader(salt, iv, hmacKey)
if _, err := dst.Write(header); err != nil {
return err
}
mac := hmac.New(c.HashFunc, hmacKey)
mac.Write(header)
block, err := aes.NewCipher(aesKey)
if err != nil {
return err
}
stream := cipher.NewCTR(block, iv)
buf := make([]byte, c.bufSize())
n := 0
for {
n, err = src.Read(buf)
if err != nil {
if err == io.EOF {
break
}
return err
}
mac.Write(buf[:n])
stream.XORKeyStream(buf[:n], buf[:n])
if _, err = dst.Write(buf[:n]); err != nil {
return err
}
if _, err = dst.Write(mac.Sum(nil)); err != nil {
return err
}
}
return nil
}
// Decrypt decrypts from src until either EOF is reached on src or an
// error occurs. A successful Decrypt returns err == nil, not err == EOF.
func (c *Crypter) Decrypt(dst io.Writer, src io.Reader) (err error) {
keySize := c.Key.Size()
headerSize := 1 + keySize + blockSize + c.HashSize
header := make([]byte, headerSize)
if _, err = src.Read(header); err != nil {
return err
}
salt, iv, err := c.decHeader(header)
if err != nil {
return err
}
aesKey, hmacKey := c.Key.Derive(salt)
mac := hmac.New(c.HashFunc, hmacKey)
mac.Write(header[:1+keySize+blockSize])
if !bytes.Equal(header[1+keySize+blockSize:], mac.Sum(nil)) {
return errors.New("cannot authenticate header")
}
mac.Write(header[1+keySize+blockSize:])
block, err := aes.NewCipher(aesKey)
if err != nil {
return err
}
stream := cipher.NewCTR(block, iv)
buf := make([]byte, c.bufSize()+c.HashSize)
n := 0
for {
n, err = src.Read(buf)
if err != nil {
if err == io.EOF {
break
}
return err
}
stream.XORKeyStream(buf[:n-c.HashSize], buf[:n-c.HashSize])
mac.Write(buf[:n-c.HashSize])
if !bytes.Equal(buf[n-c.HashSize:n], mac.Sum(nil)) {
return errors.New("cannot authenticate packet")
}
if _, err = dst.Write(buf[:n-c.HashSize]); err != nil {
return err
}
}
return nil
}

82
vendor/github.com/mars9/crypt/crypt_test.go generated vendored Normal file
View File

@ -0,0 +1,82 @@
package crypt
import (
"bytes"
"crypto/sha1"
"io/ioutil"
"os"
"testing"
)
var plain = [][]byte{
[]byte("Nö, ich trinke keinen Tee, ich bin Atheist. --- Helge Schneider"),
[]byte("I wish these damn scientists would leave intelligence to the experts. --- Gen. Richard Stillwell (CIA)"),
[]byte("I want to die peacefully in my sleep like my grandfather, not screaming in terror like his passengers. --- Charlie Hall"),
[]byte("NOTE 3: Each bit has the value either ZERO or ONE. --- ECMA-035 spec"),
[]byte("Writing about music is like dancing about architecture. --- Frank Zappa"),
[]byte("If you want to go somewhere, goto is the best way to get there. --- K Thompson"),
}
func TestEncryptDecrypt(t *testing.T) {
enc := bytes.NewBuffer(nil)
dec := bytes.NewBuffer(nil)
password := []byte("test password")
c := &Crypter{
HashFunc: sha1.New,
HashSize: sha1.Size,
Key: NewPbkdf2Key(password, 32),
}
defer c.Key.Reset()
for _, src := range plain {
enc.Reset()
dec.Reset()
err := c.Encrypt(enc, bytes.NewReader(src))
if err != nil {
t.Fatal(err)
}
err = c.Decrypt(dec, enc)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(dec.Bytes(), src) != 0 {
t.Errorf("encrypt/decrypt error: want %q, got %q", string(src), string(dec.Bytes()))
}
}
}
func TestEncryptDecrypt1(t *testing.T) {
f, err := os.Open("crypt_test.go")
if err != nil {
t.Fatal(err)
}
defer f.Close()
encBuf := bytes.NewBuffer(nil)
password := []byte("test password")
c := &Crypter{
HashFunc: sha1.New,
HashSize: sha1.Size,
Key: NewPbkdf2Key(password, 32),
}
defer c.Key.Reset()
err = c.Encrypt(encBuf, f)
if err != nil {
t.Fatal(err)
}
decBuf := bytes.NewBuffer(nil)
err = c.Decrypt(decBuf, encBuf)
if err != nil {
t.Fatal(err)
}
src, err := ioutil.ReadFile("crypt_test.go")
if err != nil {
t.Fatal(err)
}
if bytes.Compare(decBuf.Bytes(), src) != 0 {
t.Errorf("encrypt/decrypt file error: crypt_test.go")
}
}

170
vendor/github.com/mars9/crypt/crypto/main.go generated vendored Normal file
View File

@ -0,0 +1,170 @@
package main
import (
"bytes"
"crypto/sha1"
"flag"
"fmt"
"os"
"runtime"
"github.com/mars9/crypt"
"github.com/mars9/keyring"
"github.com/mars9/passwd"
)
var (
prompt = flag.Bool("p", false, "prompt to enter a passphrase")
decrypt = flag.Bool("d", false, "decrypt infile to oufile")
service = flag.String("s", "go-crypto", "keyring service name")
username = flag.String("u", os.Getenv("USER"), "keyring username")
initKeyring = flag.Bool("i", false, "intialize keyring")
)
func passphrase() ([]byte, error) {
if *prompt {
password, err := passwd.Get("Enter passphrase: ")
if err != nil {
return nil, fmt.Errorf("get passphrase: %v\n", err)
}
if !*decrypt {
confirm, err := passwd.Get("Confirm passphrase: ")
if err != nil {
return nil, fmt.Errorf("get passphrase: %v\n", err)
}
if !bytes.Equal(password, confirm) {
return nil, fmt.Errorf("Passphrase mismatch, try again.")
}
}
return password, nil
}
ring, err := keyring.New()
if err != nil {
return nil, err
}
return ring.Get(*service, *username)
}
func initialize() error {
password, err := passwd.Get("Enter passphrase: ")
if err != nil {
return fmt.Errorf("get passphrase: %v\n", err)
}
confirm, err := passwd.Get("Confirm passphrase: ")
if err != nil {
return fmt.Errorf("get passphrase: %v\n", err)
}
if !bytes.Equal(password, confirm) {
return fmt.Errorf("Passphrase mismatch, try again.")
}
ring, err := keyring.New()
if err != nil {
return err
}
return ring.Set(*service, *username, password)
}
func main() {
flag.Usage = usage
flag.Parse()
narg := flag.NArg()
if narg > 2 {
usage()
}
if runtime.GOOS == "windows" && narg == 0 {
usage()
}
if *initKeyring {
if err := initialize(); err != nil {
fmt.Fprintf(os.Stderr, "initialize keyring: %v", err)
os.Exit(1)
}
os.Exit(0)
}
password, err := passphrase()
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(3)
}
defer func() {
for i := range password {
password[i] = 0
}
}()
in := os.Stdin
out := os.Stdout
if narg > 0 {
in, err = os.Open(flag.Arg(0))
if err != nil {
fmt.Fprintf(os.Stderr, "open %s: %v\n", flag.Arg(0), err)
os.Exit(1)
}
defer in.Close()
if narg == 2 {
out, err = os.Create(flag.Arg(1))
if err != nil {
fmt.Fprintf(os.Stderr, "create %s: %v\n", flag.Arg(1), err)
os.Exit(1)
}
defer func() {
if err := out.Sync(); err != nil {
fmt.Fprintf(os.Stderr, "sync %s: %v\n", flag.Arg(1), err)
os.Exit(1)
}
if err := out.Close(); err != nil {
fmt.Fprintf(os.Stderr, "sync %s: %v\n", flag.Arg(1), err)
os.Exit(1)
}
}()
}
}
c := &crypt.Crypter{
HashFunc: sha1.New,
HashSize: sha1.Size,
Key: crypt.NewPbkdf2Key(password, 32),
}
if !*decrypt {
if err := c.Encrypt(out, in); err != nil {
fmt.Fprintf(os.Stderr, "encrypt: %v\n", err)
os.Exit(1)
}
} else {
if err := c.Decrypt(out, in); err != nil {
fmt.Fprintf(os.Stderr, "decrypt: %v\n", err)
os.Exit(1)
}
}
}
func usage() {
if runtime.GOOS == "windows" {
fmt.Fprintf(os.Stderr, "Usage: %s [options] infile [outfile]\n", os.Args[0])
} else {
fmt.Fprintf(os.Stderr, "Usage: %s [options] [infile] [[outfile]]\n", os.Args[0])
}
fmt.Fprint(os.Stderr, usageMsg)
fmt.Fprintf(os.Stderr, "\nOptions:\n")
flag.PrintDefaults()
os.Exit(2)
}
const usageMsg = `
Files are encrypted with AES (Rijndael) in cipher block counter mode
(CTR) and authenticate with HMAC-SHA. Encryption and HMAC keys are
derived from passphrase using PBKDF2.
If outfile is not specified, the de-/encrypted data is written to the
standard output and if infile is not specified, the de-/encrypted data
is read from standard input (reading standard input is not available
on windows).
`

79
vendor/github.com/mars9/crypt/key.go generated vendored Normal file
View File

@ -0,0 +1,79 @@
package crypt
import (
"crypto/sha1"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/scrypt"
)
// Key defines the key derivation function interface.
type Key interface {
// Derive returns the AES key and HMAC-SHA key, for the given password,
// salt combination.
Derive(salt []byte) (aesKey, hmacKey []byte)
// Size returns the key-size. Key-size should either 16, 24, or 32 to
// select AES-128, AES-192, or AES-256.
Size() int
// Reset resets/flushes the key.
Reset()
}
type pbkdf2Key struct {
password []byte
size int
}
// NewPbkdf2Key returns the key derivation function PBKDF2 as defined in
// RFC 2898.
func NewPbkdf2Key(password []byte, size int) Key {
return pbkdf2Key{password: password, size: size}
}
func (k pbkdf2Key) Derive(salt []byte) (aesKey, hmacKey []byte) {
key := pbkdf2.Key(k.password, salt, 4096, 2*k.size, sha1.New)
aesKey = key[:k.size]
hmacKey = key[k.size:]
return aesKey, hmacKey
}
func (k pbkdf2Key) Size() int { return k.size }
func (k pbkdf2Key) Reset() {
for i := range k.password {
k.password[i] = 0
}
}
type scryptKey struct {
password []byte
size int
}
// NewScryptKey returns the scrypt key derivation function as defined in
// Colin Percival's paper "Stronger Key Derivation via Sequential
// Memory-Hard Functions".
func NewScryptKey(password []byte, size int) Key {
return scryptKey{password: password, size: size}
}
func (k scryptKey) Derive(salt []byte) (aesKey, hmacKey []byte) {
key, err := scrypt.Key(k.password, salt, 16384, 8, 1, 2*k.size)
if err != nil {
panic(err)
}
aesKey = key[:k.size]
hmacKey = key[k.size:]
return aesKey, hmacKey
}
func (k scryptKey) Size() int { return k.size }
func (k scryptKey) Reset() {
for i := range k.password {
k.password[i] = 0
}
}

View File

@ -142,7 +142,7 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
//
// In the case where CA server does not provide the issued certificate in the response,
// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips.
// In such scenario the caller can cancel the polling with ctx.
// In such a scenario, the caller can cancel the polling with ctx.
//
// CreateCert returns an error if the CA's response or chain was unreasonably large.
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
@ -257,7 +257,7 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
func AcceptTOS(tosURL string) bool { return true }
// Register creates a new account registration by following the "new-reg" flow.
// It returns registered account. The account is not modified.
// It returns the registered account. The account is not modified.
//
// The registration may require the caller to agree to the CA's Terms of Service (TOS).
// If so, and the account has not indicated the acceptance of the terms (see Account for details),
@ -995,6 +995,7 @@ func keyAuth(pub crypto.PublicKey, token string) (string, error) {
// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
// with the given SANs and auto-generated public/private key pair.
// The Subject Common Name is set to the first SAN to aid debugging.
// To create a cert with a custom key pair, specify WithKey option.
func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
var (
@ -1033,6 +1034,9 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
}
}
tmpl.DNSNames = san
if len(san) > 0 {
tmpl.Subject.CommonName = san[0]
}
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
if err != nil {

View File

@ -1186,6 +1186,9 @@ func TestTLSSNI01ChallengeCert(t *testing.T) {
if cert.DNSNames[0] != name {
t.Errorf("cert.DNSNames[0] != name: %q vs %q", cert.DNSNames[0], name)
}
if cn := cert.Subject.CommonName; cn != san {
t.Errorf("cert.Subject.CommonName = %q; want %q", cn, san)
}
}
func TestTLSSNI02ChallengeCert(t *testing.T) {
@ -1219,6 +1222,9 @@ func TestTLSSNI02ChallengeCert(t *testing.T) {
if i >= len(cert.DNSNames) || cert.DNSNames[i] != name {
t.Errorf("%v doesn't have %q", cert.DNSNames, name)
}
if cn := cert.Subject.CommonName; cn != sanA {
t.Errorf("CommonName = %q; want %q", cn, sanA)
}
}
func TestTLSChallengeCertOpt(t *testing.T) {

View File

@ -371,7 +371,7 @@ func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certifica
// We are the first; state is locked.
// Unblock the readers when domain ownership is verified
// and the we got the cert or the process failed.
// and we got the cert or the process failed.
defer state.Unlock()
state.locked = false
@ -439,7 +439,7 @@ func (m *Manager) certState(domain string) (*certState, error) {
return state, nil
}
// authorizedCert starts domain ownership verification process and requests a new cert upon success.
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
// The key argument is the certificate private key.
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) {
if err := m.verify(ctx, domain); err != nil {

View File

@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"net"
"os"
"sync"
"time"
)
@ -187,6 +188,10 @@ func Dial(network, addr string, config *ClientConfig) (*Client, error) {
// 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 {
@ -209,6 +214,12 @@ type ClientConfig struct {
// 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
@ -255,3 +266,13 @@ 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
}
}

View File

@ -283,7 +283,9 @@ func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
}
switch packet[0] {
case msgUserAuthBanner:
// TODO(gpaul): add callback to present the banner to the user
if err := handleBannerResponse(c, packet); err != nil {
return false, err
}
case msgUserAuthPubKeyOk:
var msg userAuthPubKeyOkMsg
if err := Unmarshal(packet, &msg); err != nil {
@ -325,7 +327,9 @@ func handleAuthResponse(c packetConn) (bool, []string, error) {
switch packet[0] {
case msgUserAuthBanner:
// TODO: add callback to present the banner to the user
if err := handleBannerResponse(c, packet); err != nil {
return false, nil, err
}
case msgUserAuthFailure:
var msg userAuthFailureMsg
if err := Unmarshal(packet, &msg); err != nil {
@ -340,6 +344,24 @@ func handleAuthResponse(c packetConn) (bool, []string, error) {
}
}
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
@ -385,7 +407,9 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
// like handleAuthResponse, but with less options.
switch packet[0] {
case msgUserAuthBanner:
// TODO: Print banners during userauth.
if err := handleBannerResponse(c, packet); err != nil {
return false, nil, err
}
continue
case msgUserAuthInfoRequest:
// OK

View File

@ -79,3 +79,40 @@ func TestHostKeyCheck(t *testing.T) {
}
}
}
func TestBannerCallback(t *testing.T) {
c1, c2, err := netPipe()
if err != nil {
t.Fatalf("netPipe: %v", err)
}
defer c1.Close()
defer c2.Close()
serverConf := &ServerConfig{
NoClientAuth: true,
BannerCallback: func(conn ConnMetadata) string {
return "Hello World"
},
}
serverConf.AddHostKey(testSigners["rsa"])
go NewServerConn(c1, serverConf)
var receivedBanner string
clientConf := ClientConfig{
User: "user",
HostKeyCallback: InsecureIgnoreHostKey(),
BannerCallback: func(message string) error {
receivedBanner = message
return nil
},
}
_, _, _, err = NewClientConn(c2, "", &clientConf)
if err != nil {
t.Fatal(err)
}
expected := "Hello World"
if receivedBanner != expected {
t.Fatalf("got %s; want %s", receivedBanner, expected)
}
}

View File

@ -78,6 +78,11 @@ type handshakeTransport struct {
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
@ -120,6 +125,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt
t.dialAddress = dialAddr
t.remoteAddr = addr
t.hostKeyCallback = config.HostKeyCallback
t.bannerCallback = config.BannerCallback
if config.HostKeyAlgorithms != nil {
t.hostKeyAlgorithms = config.HostKeyAlgorithms
} else {

View File

@ -23,10 +23,6 @@ const (
msgUnimplemented = 3
msgDebug = 4
msgNewKeys = 21
// Standard authentication messages
msgUserAuthSuccess = 52
msgUserAuthBanner = 53
)
// SSH messages:
@ -137,6 +133,16 @@ type userAuthFailureMsg struct {
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"`
}
// See RFC 4256, section 3.2
const msgUserAuthInfoRequest = 60
const msgUserAuthInfoResponse = 61

View File

@ -95,6 +95,10 @@ type ServerConfig struct {
// 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
@ -343,6 +347,19 @@ userAuthLoop:
}
s.user = userAuthReq.User
if authFailures == 0 && config.BannerCallback != nil {
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")