adopt new sshpiper api
This commit is contained in:
parent
b14a776f48
commit
d8ba7b77ea
13 changed files with 936 additions and 171 deletions
11
vendor/golang.org/x/crypto/acme/autocert/autocert.go
generated
vendored
11
vendor/golang.org/x/crypto/acme/autocert/autocert.go
generated
vendored
|
|
@ -27,7 +27,6 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -942,16 +941,6 @@ func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certi
|
|||
return leaf, nil
|
||||
}
|
||||
|
||||
func retryAfter(v string) time.Duration {
|
||||
if i, err := strconv.Atoi(v); err == nil {
|
||||
return time.Duration(i) * time.Second
|
||||
}
|
||||
if t, err := http.ParseTime(v); err == nil {
|
||||
return t.Sub(timeNow())
|
||||
}
|
||||
return time.Second
|
||||
}
|
||||
|
||||
type lockedMathRand struct {
|
||||
sync.Mutex
|
||||
rnd *mathrand.Rand
|
||||
|
|
|
|||
79
vendor/golang.org/x/crypto/argon2/argon2.go
generated
vendored
79
vendor/golang.org/x/crypto/argon2/argon2.go
generated
vendored
|
|
@ -5,7 +5,35 @@
|
|||
// Package argon2 implements the key derivation function Argon2.
|
||||
// Argon2 was selected as the winner of the Password Hashing Competition and can
|
||||
// be used to derive cryptographic keys from passwords.
|
||||
// Argon2 is specfifed at https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
|
||||
//
|
||||
// For a detailed specification of Argon2 see [1].
|
||||
//
|
||||
// If you aren't sure which function you need, use Argon2id (IDKey) and
|
||||
// the parameter recommendations for your scenario.
|
||||
//
|
||||
//
|
||||
// Argon2i
|
||||
//
|
||||
// Argon2i (implemented by Key) is the side-channel resistant version of Argon2.
|
||||
// It uses data-independent memory access, which is preferred for password
|
||||
// hashing and password-based key derivation. Argon2i requires more passes over
|
||||
// memory than Argon2id to protect from trade-off attacks. The recommended
|
||||
// parameters (taken from [2]) for non-interactive operations are time=3 and to
|
||||
// use the maximum available memory.
|
||||
//
|
||||
//
|
||||
// Argon2id
|
||||
//
|
||||
// Argon2id (implemented by IDKey) is a hybrid version of Argon2 combining
|
||||
// Argon2i and Argon2d. It uses data-independent memory access for the first
|
||||
// half of the first iteration over the memory and data-dependent memory access
|
||||
// for the rest. Argon2id is side-channel resistant and provides better brute-
|
||||
// force cost savings due to time-memory tradeoffs than Argon2i. The recommended
|
||||
// parameters for non-interactive operations (taken from [2]) are time=1 and to
|
||||
// use the maximum available memory.
|
||||
//
|
||||
// [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
|
||||
// [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3
|
||||
package argon2
|
||||
|
||||
import (
|
||||
|
|
@ -25,23 +53,50 @@ const (
|
|||
)
|
||||
|
||||
// Key derives a key from the password, salt, and cost parameters using Argon2i
|
||||
// returning a byte slice of length keyLen that can be used as cryptographic key.
|
||||
// The CPU cost and parallism degree must be greater than zero.
|
||||
// returning a byte slice of length keyLen that can be used as cryptographic
|
||||
// key. The CPU cost and parallism degree must be greater than zero.
|
||||
//
|
||||
// For example, you can get a derived key for e.g. AES-256 (which needs a 32-byte key) by doing:
|
||||
// `key := argon2.Key([]byte("some password"), salt, 4, 32*1024, 4, 32)`
|
||||
// For example, you can get a derived key for e.g. AES-256 (which needs a
|
||||
// 32-byte key) by doing: `key := argon2.Key([]byte("some password"), salt, 3,
|
||||
// 32*1024, 4, 32)`
|
||||
//
|
||||
// The recommended parameters for interactive logins as of 2017 are time=4, memory=32*1024.
|
||||
// The number of threads can be adjusted to the numbers of available CPUs.
|
||||
// The time parameter specifies the number of passes over the memory and the memory
|
||||
// parameter specifies the size of the memory in KiB. For example memory=32*1024 sets the
|
||||
// memory cost to ~32 MB.
|
||||
// The cost parameters should be increased as memory latency and CPU parallelism increases.
|
||||
// Remember to get a good random salt.
|
||||
// The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number.
|
||||
// If using that amount of memory (32 MB) is not possible in some contexts then
|
||||
// the time parameter can be increased to compensate.
|
||||
//
|
||||
// The time parameter specifies the number of passes over the memory and the
|
||||
// memory parameter specifies the size of the memory in KiB. For example
|
||||
// memory=32*1024 sets the memory cost to ~32 MB. The number of threads can be
|
||||
// adjusted to the number of available CPUs. The cost parameters should be
|
||||
// increased as memory latency and CPU parallelism increases. Remember to get a
|
||||
// good random salt.
|
||||
func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||
return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
|
||||
}
|
||||
|
||||
// IDKey derives a key from the password, salt, and cost parameters using
|
||||
// Argon2id returning a byte slice of length keyLen that can be used as
|
||||
// cryptographic key. The CPU cost and parallism degree must be greater than
|
||||
// zero.
|
||||
//
|
||||
// For example, you can get a derived key for e.g. AES-256 (which needs a
|
||||
// 32-byte key) by doing: `key := argon2.IDKey([]byte("some password"), salt, 1,
|
||||
// 64*1024, 4, 32)`
|
||||
//
|
||||
// The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
|
||||
// If using that amount of memory (64 MB) is not possible in some contexts then
|
||||
// the time parameter can be increased to compensate.
|
||||
//
|
||||
// The time parameter specifies the number of passes over the memory and the
|
||||
// memory parameter specifies the size of the memory in KiB. For example
|
||||
// memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be
|
||||
// adjusted to the numbers of available CPUs. The cost parameters should be
|
||||
// increased as memory latency and CPU parallelism increases. Remember to get a
|
||||
// good random salt.
|
||||
func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||
return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
|
||||
}
|
||||
|
||||
func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||
if time < 1 {
|
||||
panic("argon2: number of rounds too small")
|
||||
|
|
|
|||
4
vendor/golang.org/x/crypto/ssh/certs.go
generated
vendored
4
vendor/golang.org/x/crypto/ssh/certs.go
generated
vendored
|
|
@ -44,7 +44,9 @@ type Signature struct {
|
|||
const CertTimeInfinity = 1<<64 - 1
|
||||
|
||||
// An Certificate represents an OpenSSH certificate as defined in
|
||||
// [PROTOCOL.certkeys]?rev=1.8.
|
||||
// [PROTOCOL.certkeys]?rev=1.8. The Certificate type implements the
|
||||
// PublicKey interface, so it can be unmarshaled using
|
||||
// ParsePublicKey.
|
||||
type Certificate struct {
|
||||
Nonce []byte
|
||||
Key PublicKey
|
||||
|
|
|
|||
95
vendor/golang.org/x/crypto/ssh/client_auth.go
generated
vendored
95
vendor/golang.org/x/crypto/ssh/client_auth.go
generated
vendored
|
|
@ -11,6 +11,14 @@ import (
|
|||
"io"
|
||||
)
|
||||
|
||||
type authResult int
|
||||
|
||||
const (
|
||||
authFailure authResult = iota
|
||||
authPartialSuccess
|
||||
authSuccess
|
||||
)
|
||||
|
||||
// clientAuthenticate authenticates with the remote server. See RFC 4252.
|
||||
func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
// initiate user auth session
|
||||
|
|
@ -37,11 +45,12 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
if ok == authSuccess {
|
||||
// success
|
||||
return nil
|
||||
} else if ok == authFailure {
|
||||
tried[auth.method()] = true
|
||||
}
|
||||
tried[auth.method()] = true
|
||||
if methods == nil {
|
||||
methods = lastMethods
|
||||
}
|
||||
|
|
@ -82,7 +91,7 @@ type AuthMethod interface {
|
|||
// 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)
|
||||
auth(session []byte, user string, p packetConn, rand io.Reader) (authResult, []string, error)
|
||||
|
||||
// method returns the RFC 4252 method name.
|
||||
method() string
|
||||
|
|
@ -91,13 +100,13 @@ type AuthMethod interface {
|
|||
// "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) {
|
||||
func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
|
||||
if err := c.writePacket(Marshal(&userAuthRequestMsg{
|
||||
User: user,
|
||||
Service: serviceSSH,
|
||||
Method: "none",
|
||||
})); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
return handleAuthResponse(c)
|
||||
|
|
@ -111,7 +120,7 @@ func (n *noneAuth) method() string {
|
|||
// 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) {
|
||||
func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
|
||||
type passwordAuthMsg struct {
|
||||
User string `sshtype:"50"`
|
||||
Service string
|
||||
|
|
@ -125,7 +134,7 @@ func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand
|
|||
// The program may only find out that the user doesn't have a password
|
||||
// when prompting.
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
if err := c.writePacket(Marshal(&passwordAuthMsg{
|
||||
|
|
@ -135,7 +144,7 @@ func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand
|
|||
Reply: false,
|
||||
Password: pw,
|
||||
})); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
return handleAuthResponse(c)
|
||||
|
|
@ -178,7 +187,7 @@ func (cb publicKeyCallback) method() string {
|
|||
return "publickey"
|
||||
}
|
||||
|
||||
func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
|
||||
func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []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
|
||||
|
|
@ -186,13 +195,13 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
|||
|
||||
signers, err := cb()
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
var methods []string
|
||||
for _, signer := range signers {
|
||||
ok, err := validateKey(signer.PublicKey(), user, c)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
|
|
@ -206,7 +215,7 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
|||
Method: cb.method(),
|
||||
}, []byte(pub.Type()), pubKey))
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
// manually wrap the serialized signature in a string
|
||||
|
|
@ -224,24 +233,24 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
|||
}
|
||||
p := Marshal(&msg)
|
||||
if err := c.writePacket(p); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
var success bool
|
||||
var success authResult
|
||||
success, methods, err = handleAuthResponse(c)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, 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()) {
|
||||
if success == authSuccess || !containsMethod(methods, cb.method()) {
|
||||
return success, methods, err
|
||||
}
|
||||
}
|
||||
|
||||
return false, methods, nil
|
||||
return authFailure, methods, nil
|
||||
}
|
||||
|
||||
func containsMethod(methods []string, method string) bool {
|
||||
|
|
@ -318,28 +327,31 @@ func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMet
|
|||
// 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) {
|
||||
func handleAuthResponse(c packetConn) (authResult, []string, error) {
|
||||
for {
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
switch packet[0] {
|
||||
case msgUserAuthBanner:
|
||||
if err := handleBannerResponse(c, packet); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
case msgUserAuthFailure:
|
||||
var msg userAuthFailureMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
return false, msg.Methods, nil
|
||||
if msg.PartialSuccess {
|
||||
return authPartialSuccess, msg.Methods, nil
|
||||
}
|
||||
return authFailure, msg.Methods, nil
|
||||
case msgUserAuthSuccess:
|
||||
return true, nil, nil
|
||||
return authSuccess, nil, nil
|
||||
default:
|
||||
return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
|
||||
return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -381,7 +393,7 @@ func (cb KeyboardInteractiveChallenge) method() string {
|
|||
return "keyboard-interactive"
|
||||
}
|
||||
|
||||
func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
|
||||
func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
|
||||
type initiateMsg struct {
|
||||
User string `sshtype:"50"`
|
||||
Service string
|
||||
|
|
@ -395,20 +407,20 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
|||
Service: serviceSSH,
|
||||
Method: "keyboard-interactive",
|
||||
})); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
for {
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
// like handleAuthResponse, but with less options.
|
||||
switch packet[0] {
|
||||
case msgUserAuthBanner:
|
||||
if err := handleBannerResponse(c, packet); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
continue
|
||||
case msgUserAuthInfoRequest:
|
||||
|
|
@ -416,18 +428,21 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
|||
case msgUserAuthFailure:
|
||||
var msg userAuthFailureMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
return false, msg.Methods, nil
|
||||
if msg.PartialSuccess {
|
||||
return authPartialSuccess, msg.Methods, nil
|
||||
}
|
||||
return authFailure, msg.Methods, nil
|
||||
case msgUserAuthSuccess:
|
||||
return true, nil, nil
|
||||
return authSuccess, nil, nil
|
||||
default:
|
||||
return false, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
|
||||
return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
|
||||
}
|
||||
|
||||
var msg userAuthInfoRequestMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
// Manually unpack the prompt/echo pairs.
|
||||
|
|
@ -437,7 +452,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
|||
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")
|
||||
return authFailure, nil, errors.New("ssh: prompt format error")
|
||||
}
|
||||
prompts = append(prompts, string(prompt))
|
||||
echos = append(echos, r[0] != 0)
|
||||
|
|
@ -445,16 +460,16 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
|||
}
|
||||
|
||||
if len(rest) != 0 {
|
||||
return false, nil, errors.New("ssh: extra data following keyboard-interactive pairs")
|
||||
return authFailure, 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
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
if len(answers) != len(prompts) {
|
||||
return false, nil, errors.New("ssh: not enough answers from keyboard-interactive callback")
|
||||
return authFailure, nil, errors.New("ssh: not enough answers from keyboard-interactive callback")
|
||||
}
|
||||
responseLength := 1 + 4
|
||||
for _, a := range answers {
|
||||
|
|
@ -470,7 +485,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
|||
}
|
||||
|
||||
if err := c.writePacket(serialized); err != nil {
|
||||
return false, nil, err
|
||||
return authFailure, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -480,10 +495,10 @@ type retryableAuthMethod struct {
|
|||
maxTries int
|
||||
}
|
||||
|
||||
func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok bool, methods []string, err error) {
|
||||
func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok authResult, 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
|
||||
if ok != authFailure || err != nil { // either success, partial success or error terminate
|
||||
return ok, methods, err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
24
vendor/golang.org/x/crypto/ssh/keys_test.go
generated
vendored
24
vendor/golang.org/x/crypto/ssh/keys_test.go
generated
vendored
|
|
@ -234,7 +234,7 @@ func TestMarshalParsePublicKey(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
type authResult struct {
|
||||
type testAuthResult struct {
|
||||
pubKey PublicKey
|
||||
options []string
|
||||
comments string
|
||||
|
|
@ -242,11 +242,11 @@ type authResult struct {
|
|||
ok bool
|
||||
}
|
||||
|
||||
func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) {
|
||||
func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []testAuthResult) {
|
||||
rest := authKeys
|
||||
var values []authResult
|
||||
var values []testAuthResult
|
||||
for len(rest) > 0 {
|
||||
var r authResult
|
||||
var r testAuthResult
|
||||
var err error
|
||||
r.pubKey, r.comments, r.options, rest, err = ParseAuthorizedKey(rest)
|
||||
r.ok = (err == nil)
|
||||
|
|
@ -264,7 +264,7 @@ func TestAuthorizedKeyBasic(t *testing.T) {
|
|||
pub, pubSerialized := getTestKey()
|
||||
line := "ssh-rsa " + pubSerialized + " user@host"
|
||||
testAuthorizedKeys(t, []byte(line),
|
||||
[]authResult{
|
||||
[]testAuthResult{
|
||||
{pub, nil, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
|
|
@ -286,7 +286,7 @@ func TestAuth(t *testing.T) {
|
|||
authOptions := strings.Join(authWithOptions, eol)
|
||||
rest2 := strings.Join(authWithOptions[3:], eol)
|
||||
rest3 := strings.Join(authWithOptions[6:], eol)
|
||||
testAuthorizedKeys(t, []byte(authOptions), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authOptions), []testAuthResult{
|
||||
{pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true},
|
||||
{pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true},
|
||||
{nil, nil, "", "", false},
|
||||
|
|
@ -297,7 +297,7 @@ func TestAuth(t *testing.T) {
|
|||
func TestAuthWithQuotedSpaceInEnv(t *testing.T) {
|
||||
pub, pubSerialized := getTestKey()
|
||||
authWithQuotedSpaceInEnv := []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`)
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []testAuthResult{
|
||||
{pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
|
|
@ -305,7 +305,7 @@ func TestAuthWithQuotedSpaceInEnv(t *testing.T) {
|
|||
func TestAuthWithQuotedCommaInEnv(t *testing.T) {
|
||||
pub, pubSerialized := getTestKey()
|
||||
authWithQuotedCommaInEnv := []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`)
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []testAuthResult{
|
||||
{pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
|
|
@ -314,11 +314,11 @@ func TestAuthWithQuotedQuoteInEnv(t *testing.T) {
|
|||
pub, pubSerialized := getTestKey()
|
||||
authWithQuotedQuoteInEnv := []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + pubSerialized + ` user@host`)
|
||||
authWithDoubleQuotedQuote := []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + pubSerialized + "\t" + `user@host`)
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []testAuthResult{
|
||||
{pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true},
|
||||
})
|
||||
|
||||
testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []testAuthResult{
|
||||
{pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
|
|
@ -327,7 +327,7 @@ func TestAuthWithInvalidSpace(t *testing.T) {
|
|||
_, pubSerialized := getTestKey()
|
||||
authWithInvalidSpace := []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
|
||||
#more to follow but still no valid keys`)
|
||||
testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authWithInvalidSpace), []testAuthResult{
|
||||
{nil, nil, "", "", false},
|
||||
})
|
||||
}
|
||||
|
|
@ -337,7 +337,7 @@ func TestAuthWithMissingQuote(t *testing.T) {
|
|||
authWithMissingQuote := []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
|
||||
env="HOME=/home/root",shared-control ssh-rsa ` + pubSerialized + ` user@host`)
|
||||
|
||||
testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{
|
||||
testAuthorizedKeys(t, []byte(authWithMissingQuote), []testAuthResult{
|
||||
{pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
228
vendor/golang.org/x/crypto/ssh/sshpiper.go
generated
vendored
228
vendor/golang.org/x/crypto/ssh/sshpiper.go
generated
vendored
|
|
@ -11,6 +11,31 @@ import (
|
|||
"net"
|
||||
)
|
||||
|
||||
type AuthPipeType int
|
||||
|
||||
const (
|
||||
AuthPipeTypePassThrough AuthPipeType = iota
|
||||
AuthPipeTypeMap
|
||||
AuthPipeTypeDiscard
|
||||
AuthPipeTypeNone
|
||||
)
|
||||
|
||||
type SSHPiperAuthPipe struct {
|
||||
User string
|
||||
|
||||
PasswordCallback func(conn ConnMetadata, password []byte) (AuthPipeType, AuthMethod, error)
|
||||
|
||||
PublicKeyCallback func(conn ConnMetadata, key PublicKey) (AuthPipeType, AuthMethod, error)
|
||||
|
||||
// HostKeyCallback is called during the cryptographic
|
||||
// handshake to validate the uptream server's host key. The piper
|
||||
// configuration must supply this callback for the connection
|
||||
// to succeed. The functions InsecureIgnoreHostKey or
|
||||
// FixedHostKey can be used for simplistic host key checks.
|
||||
// UpstreamHostKeyCallback HostKeyCallback
|
||||
UpstreamHostKeyCallback HostKeyCallback
|
||||
}
|
||||
|
||||
// SSHPiperConfig holds SSHPiper specific configuration data.
|
||||
type SSHPiperConfig struct {
|
||||
Config
|
||||
|
|
@ -28,7 +53,7 @@ type SSHPiperConfig struct {
|
|||
// and upstream username should be returned.
|
||||
// SSHPiper will use the username from downstream if empty username is returned.
|
||||
// If any error occurs, the piped connection will be closed.
|
||||
FindUpstream func(conn ConnMetadata) (net.Conn, string, error)
|
||||
FindUpstream func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error)
|
||||
|
||||
// MapPublicKey, if non-nil, is called when downstream requests a publickey auth.
|
||||
// SSHPiper will sign the auth packet message using the returned Signer.
|
||||
|
|
@ -38,14 +63,8 @@ type SSHPiperConfig struct {
|
|||
// upstream ssh server instead.
|
||||
//
|
||||
// More info: https://github.com/tg123/sshpiper#publickey-sign-again
|
||||
MapPublicKey func(conn ConnMetadata, key PublicKey) (Signer, error)
|
||||
// MapPublicKey func(conn ConnMetadata, key PublicKey) (Signer, error)
|
||||
|
||||
// HostKeyCallback is called during the cryptographic
|
||||
// handshake to validate the uptream server's host key. The piper
|
||||
// configuration must supply this callback for the connection
|
||||
// to succeed. The functions InsecureIgnoreHostKey or
|
||||
// FixedHostKey can be used for simplistic host key checks.
|
||||
UpstreamHostKeyCallback HostKeyCallback
|
||||
}
|
||||
|
||||
type upstream struct{ *connection }
|
||||
|
|
@ -132,9 +151,10 @@ func NewSSHPiperConn(conn net.Conn, piper *SSHPiperConfig) (pipe *SSHPiperConn,
|
|||
return nil, errors.New("sshpiper: must specify FindUpstream")
|
||||
}
|
||||
|
||||
if piper.UpstreamHostKeyCallback == nil {
|
||||
return nil, errors.New("sshpiper: must specify UpstreamHostKeyCallback")
|
||||
}
|
||||
// TODO
|
||||
//if piper.UpstreamHostKeyCallback == nil {
|
||||
// return nil, errors.New("sshpiper: must specify UpstreamHostKeyCallback")
|
||||
//}
|
||||
|
||||
d, err := newDownstream(conn, &ServerConfig{
|
||||
Config: piper.Config,
|
||||
|
|
@ -191,11 +211,16 @@ func NewSSHPiperConn(conn net.Conn, piper *SSHPiperConfig) (pipe *SSHPiperConn,
|
|||
}
|
||||
}
|
||||
|
||||
upconn, mappedUser, err := piper.FindUpstream(d)
|
||||
upconn, pipeauth, err := piper.FindUpstream(d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pipeauth.UpstreamHostKeyCallback == nil {
|
||||
return nil, errors.New("sshpiper: must specify UpstreamHostKeyCallback")
|
||||
}
|
||||
|
||||
mappedUser := pipeauth.User
|
||||
addr := upconn.RemoteAddr().String()
|
||||
|
||||
if mappedUser == "" {
|
||||
|
|
@ -203,7 +228,7 @@ func NewSSHPiperConn(conn net.Conn, piper *SSHPiperConfig) (pipe *SSHPiperConn,
|
|||
}
|
||||
|
||||
u, err := newUpstream(upconn, addr, &ClientConfig{
|
||||
HostKeyCallback: piper.UpstreamHostKeyCallback,
|
||||
HostKeyCallback: pipeauth.UpstreamHostKeyCallback,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -221,50 +246,143 @@ func NewSSHPiperConn(conn net.Conn, piper *SSHPiperConfig) (pipe *SSHPiperConn,
|
|||
|
||||
p.processAuthMsg = func(msg *userAuthRequestMsg) (*userAuthRequestMsg, error) {
|
||||
|
||||
// only public msg need
|
||||
if msg.Method != "publickey" || piper.MapPublicKey == nil {
|
||||
msg.User = mappedUser
|
||||
msg.User = mappedUser
|
||||
|
||||
var authType AuthPipeType = AuthPipeTypePassThrough
|
||||
var authMethod AuthMethod
|
||||
|
||||
switch msg.Method {
|
||||
case "publickey":
|
||||
|
||||
if pipeauth.PublicKeyCallback == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// pubKey MAP
|
||||
downKey, isQuery, sig, err := parsePublicKeyMsg(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authType, authMethod, err = pipeauth.PublicKeyCallback(d, downKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isQuery {
|
||||
// reply for query msg
|
||||
// skip query from upstream
|
||||
err = p.ack(downKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// discard msg
|
||||
return nil, nil
|
||||
} else {
|
||||
|
||||
ok, err := p.checkPublicKey(msg, downKey, sig)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return noneAuthMsg(mappedUser), nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case "password":
|
||||
if pipeauth.PasswordCallback == nil {
|
||||
break
|
||||
}
|
||||
|
||||
payload := msg.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)
|
||||
}
|
||||
|
||||
authType, authMethod, err = pipeauth.PasswordCallback(d, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
switch authType {
|
||||
case AuthPipeTypePassThrough:
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// pubKey MAP
|
||||
downKey, isQuery, sig, err := parsePublicKeyMsg(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signer, err := piper.MapPublicKey(d, downKey)
|
||||
|
||||
// no mapped user change it to none or error occur
|
||||
if err != nil || signer == nil {
|
||||
case AuthPipeTypeDiscard:
|
||||
return msg, nil
|
||||
case AuthPipeTypeNone:
|
||||
return noneAuthMsg(mappedUser), nil
|
||||
case AuthPipeTypeMap:
|
||||
}
|
||||
|
||||
upKey := signer.PublicKey()
|
||||
// map publickey, password, etc to auth
|
||||
|
||||
if isQuery {
|
||||
// reply for query msg
|
||||
msg, err = p.validAndAck(mappedUser, upKey, downKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch authMethod.method() {
|
||||
case "publickey":
|
||||
|
||||
} else {
|
||||
|
||||
ok, err := p.checkPublicKey(msg, downKey, sig)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, ok := authMethod.(publicKeyCallback)
|
||||
|
||||
if !ok {
|
||||
return noneAuthMsg(mappedUser), nil
|
||||
return nil, errors.New("sshpiper: publicKeyCallback type assertions failed")
|
||||
}
|
||||
|
||||
msg, err = p.signAgain(mappedUser, msg, signer, downKey)
|
||||
signers, err := f()
|
||||
// no mapped user change it to none or error occur
|
||||
//if err != nil || len(signers) == 0 {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, signer := range signers {
|
||||
msg, err = p.signAgain(mappedUser, msg, signer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case "password":
|
||||
|
||||
f, ok := authMethod.(passwordCallback)
|
||||
|
||||
if !ok {
|
||||
return nil, errors.New("sshpiper: passwordCallback type assertions failed")
|
||||
}
|
||||
|
||||
pw, err := f()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
type passwordAuthMsg struct {
|
||||
User string `sshtype:"50"`
|
||||
Service string
|
||||
Method string
|
||||
Reply bool
|
||||
Password string
|
||||
}
|
||||
|
||||
Unmarshal(Marshal(passwordAuthMsg{
|
||||
User: mappedUser,
|
||||
Service: serviceSSH,
|
||||
Method: "password",
|
||||
Reply: false,
|
||||
Password: pw,
|
||||
}), msg)
|
||||
|
||||
return msg, nil
|
||||
|
||||
default:
|
||||
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
|
|
@ -278,17 +396,27 @@ func NewSSHPiperConn(conn net.Conn, piper *SSHPiperConfig) (pipe *SSHPiperConn,
|
|||
return &SSHPiperConn{pipedConn: p}, nil
|
||||
}
|
||||
|
||||
func (pipe *pipedConn) ack(key PublicKey) error {
|
||||
okMsg := userAuthPubKeyOkMsg{
|
||||
Algo: key.Type(),
|
||||
PubKey: key.Marshal(),
|
||||
}
|
||||
|
||||
if err := pipe.downstream.transport.writePacket(Marshal(&okMsg)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// not used after method to method map enable
|
||||
func (pipe *pipedConn) validAndAck(user string, upKey, downKey PublicKey) (*userAuthRequestMsg, error) {
|
||||
|
||||
ok, err := validateKey(upKey, user, pipe.upstream.transport)
|
||||
|
||||
if ok {
|
||||
okMsg := userAuthPubKeyOkMsg{
|
||||
Algo: downKey.Type(),
|
||||
PubKey: downKey.Marshal(),
|
||||
}
|
||||
|
||||
if err = pipe.downstream.transport.writePacket(Marshal(&okMsg)); err != nil {
|
||||
if err = pipe.ack(downKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -312,7 +440,7 @@ func (pipe *pipedConn) checkPublicKey(msg *userAuthRequestMsg, pubkey PublicKey,
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func (pipe *pipedConn) signAgain(user string, msg *userAuthRequestMsg, signer Signer, downKey PublicKey) (*userAuthRequestMsg, error) {
|
||||
func (pipe *pipedConn) signAgain(user string, msg *userAuthRequestMsg, signer Signer) (*userAuthRequestMsg, error) {
|
||||
|
||||
rand := pipe.upstream.transport.config.Rand
|
||||
session := pipe.upstream.transport.getSessionID()
|
||||
|
|
|
|||
237
vendor/golang.org/x/crypto/ssh/sshpiper_test.go
generated
vendored
237
vendor/golang.org/x/crypto/ssh/sshpiper_test.go
generated
vendored
|
|
@ -22,16 +22,18 @@ func ExampleNewSSHPiperConn() {
|
|||
|
||||
piper := &SSHPiperConfig{
|
||||
// return conn dial to serverAddr
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, string, error) {
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) {
|
||||
c, err := net.Dial("tcp", serverAddr)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// change upstream username to root
|
||||
return c, "root", nil
|
||||
return c, &SSHPiperAuthPipe{
|
||||
User: "root",
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}, nil
|
||||
},
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
// add private key
|
||||
|
|
@ -94,14 +96,14 @@ func dialPiper(piper *SSHPiperConfig) (net.Conn, error) {
|
|||
return c, nil
|
||||
}
|
||||
|
||||
func TestFindUpstreamCallback(t *testing.T) {
|
||||
func TestPiperFindUpstreamCallback(t *testing.T) {
|
||||
|
||||
const username = "testuser"
|
||||
|
||||
var called bool
|
||||
|
||||
c, err := dialPiper(&SSHPiperConfig{
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, string, error) {
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) {
|
||||
if username != conn.User() {
|
||||
t.Errorf("different username")
|
||||
}
|
||||
|
|
@ -114,13 +116,18 @@ func TestFindUpstreamCallback(t *testing.T) {
|
|||
t.Errorf("default username changed")
|
||||
}
|
||||
|
||||
if string(password) != "password" {
|
||||
t.Errorf("password not equal")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}, t)
|
||||
|
||||
return s, "", err
|
||||
return s, &SSHPiperAuthPipe{
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}, err
|
||||
},
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -140,14 +147,14 @@ func TestFindUpstreamCallback(t *testing.T) {
|
|||
}
|
||||
|
||||
// TODO clean up duplicate code
|
||||
func TestFindUpstreamWithUserCallback(t *testing.T) {
|
||||
func TestPiperFindUpstreamWithUserCallback(t *testing.T) {
|
||||
const username = "testuser"
|
||||
const mappedname = "mappedname"
|
||||
|
||||
var called bool
|
||||
|
||||
c, err := dialPiper(&SSHPiperConfig{
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, string, error) {
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) {
|
||||
|
||||
s, err := dialUpstream(simpleEchoHandler, &ServerConfig{
|
||||
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||
|
|
@ -161,9 +168,11 @@ func TestFindUpstreamWithUserCallback(t *testing.T) {
|
|||
},
|
||||
}, t)
|
||||
|
||||
return s, mappedname, err
|
||||
return s, &SSHPiperAuthPipe{
|
||||
User: mappedname,
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}, err
|
||||
},
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -181,7 +190,7 @@ func TestFindUpstreamWithUserCallback(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestMapPublicKey(t *testing.T) {
|
||||
func TestPiperMapPublicKey(t *testing.T) {
|
||||
|
||||
certChecker := CertChecker{
|
||||
IsUserAuthority: func(k PublicKey) bool {
|
||||
|
|
@ -200,17 +209,19 @@ func TestMapPublicKey(t *testing.T) {
|
|||
}
|
||||
|
||||
c, err := dialPiper(&SSHPiperConfig{
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, string, error) {
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) {
|
||||
s, err := dialUpstream(simpleEchoHandler, &ServerConfig{
|
||||
PublicKeyCallback: certChecker.Authenticate,
|
||||
}, t)
|
||||
return s, "", err
|
||||
},
|
||||
return s, &SSHPiperAuthPipe{
|
||||
|
||||
MapPublicKey: func(conn ConnMetadata, key PublicKey) (Signer, error) {
|
||||
return testSigners["rsa"], nil
|
||||
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (AuthPipeType, AuthMethod, error) {
|
||||
return AuthPipeTypeMap, PublicKeys(testSigners["rsa"]), nil
|
||||
},
|
||||
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}, err
|
||||
},
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -230,7 +241,177 @@ func TestMapPublicKey(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAdditionalChallenge(t *testing.T) {
|
||||
func TestPiperMapPublicKeyToPassword(t *testing.T) {
|
||||
certChecker := CertChecker{
|
||||
IsUserAuthority: func(k PublicKey) bool {
|
||||
return bytes.Equal(k.Marshal(), testPublicKeys["ecdsa"].Marshal())
|
||||
},
|
||||
UserKeyFallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
||||
if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("pubkey for %q not acceptable", conn.User())
|
||||
},
|
||||
IsRevoked: func(c *Certificate) bool {
|
||||
return c.Serial == 666
|
||||
},
|
||||
}
|
||||
|
||||
var called bool
|
||||
|
||||
c, err := dialPiper(&SSHPiperConfig{
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) {
|
||||
s, err := dialUpstream(simpleEchoHandler, &ServerConfig{
|
||||
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||
t.Errorf("PasswordCallback should not be called")
|
||||
return nil, nil
|
||||
},
|
||||
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
||||
|
||||
called = true
|
||||
return certChecker.Authenticate(conn, key)
|
||||
},
|
||||
}, t)
|
||||
return s, &SSHPiperAuthPipe{
|
||||
PasswordCallback: func(conn ConnMetadata, password []byte) (AuthPipeType, AuthMethod, error) {
|
||||
if string(password) != "mypassword" {
|
||||
t.Errorf("password not equal")
|
||||
}
|
||||
|
||||
return AuthPipeTypeMap, PublicKeys(testSigners["rsa"]), nil
|
||||
},
|
||||
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}, err
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("connect dial to piper: %v", err)
|
||||
}
|
||||
|
||||
_, _, _, err = NewClientConn(c, "", &ClientConfig{
|
||||
User: "testuser",
|
||||
Auth: []AuthMethod{
|
||||
Password("mypassword"),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("can connect to piper %v", err)
|
||||
}
|
||||
|
||||
if !called {
|
||||
t.Fatalf("PublicKeyCallback not called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPiperPasswordToMapPublicKey(t *testing.T) {
|
||||
var called bool
|
||||
|
||||
c, err := dialPiper(&SSHPiperConfig{
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) {
|
||||
s, err := dialUpstream(simpleEchoHandler, &ServerConfig{
|
||||
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||
called = true
|
||||
|
||||
if string(password) != "mypassword" {
|
||||
t.Errorf("password not equal")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
||||
|
||||
t.Errorf("PublicKeyCallback should not be called")
|
||||
return nil, nil
|
||||
},
|
||||
}, t)
|
||||
return s, &SSHPiperAuthPipe{
|
||||
|
||||
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (AuthPipeType, AuthMethod, error) {
|
||||
return AuthPipeTypeMap, Password("mypassword"), nil
|
||||
},
|
||||
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}, err
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("connect dial to piper: %v", err)
|
||||
}
|
||||
|
||||
_, _, _, err = NewClientConn(c, "", &ClientConfig{
|
||||
User: "testuser",
|
||||
Auth: []AuthMethod{
|
||||
PublicKeys(testSigners["rsa"]),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("can connect to piper %v", err)
|
||||
}
|
||||
|
||||
if !called {
|
||||
t.Fatalf("PasswordCallback not called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPiperServerWithBanner(t *testing.T) {
|
||||
|
||||
const username = "testuser"
|
||||
|
||||
var called bool
|
||||
|
||||
c, err := dialPiper(&SSHPiperConfig{
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) {
|
||||
if username != conn.User() {
|
||||
t.Errorf("different username")
|
||||
}
|
||||
|
||||
s, err := dialUpstream(simpleEchoHandler, &ServerConfig{
|
||||
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||
return nil, nil
|
||||
},
|
||||
BannerCallback: func(conn ConnMetadata) string {
|
||||
called = true
|
||||
return "banner"
|
||||
},
|
||||
}, t)
|
||||
|
||||
return s, &SSHPiperAuthPipe{
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}, err
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("connect dial to piper: %v", err)
|
||||
}
|
||||
|
||||
NewClientConn(c, "", &ClientConfig{
|
||||
User: username,
|
||||
Auth: []AuthMethod{Password("password")},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
BannerCallback: func(message string) error {
|
||||
if message != "banner" {
|
||||
t.Errorf("bad banner string")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
if !called {
|
||||
t.Fatalf("FindUpstream not called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPiperAdditionalChallenge(t *testing.T) {
|
||||
c, err := dialPiper(&SSHPiperConfig{
|
||||
AdditionalChallenge: func(conn ConnMetadata, challenge KeyboardInteractiveChallenge) (bool, error) {
|
||||
ans, err := challenge("user",
|
||||
|
|
@ -249,11 +430,12 @@ func TestAdditionalChallenge(t *testing.T) {
|
|||
}
|
||||
return false, fmt.Errorf("keyboard-interactive failed")
|
||||
},
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, string, error) {
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) {
|
||||
s, err := dialUpstream(simpleEchoHandler, &ServerConfig{NoClientAuth: true}, t)
|
||||
return s, "", err
|
||||
return s, &SSHPiperAuthPipe{
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}, err
|
||||
},
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -320,14 +502,15 @@ func dialUpstream(handler serverType, upstream *ServerConfig, t *testing.T) (net
|
|||
return c, nil
|
||||
}
|
||||
|
||||
func TestPipeData(t *testing.T) {
|
||||
func TestPiperPipeData(t *testing.T) {
|
||||
|
||||
c, err := dialPiper(&SSHPiperConfig{
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, string, error) {
|
||||
FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) {
|
||||
s, err := dialUpstream(simpleEchoHandler, &ServerConfig{NoClientAuth: true}, t)
|
||||
return s, "", err
|
||||
return s, &SSHPiperAuthPipe{
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}, err
|
||||
},
|
||||
UpstreamHostKeyCallback: InsecureIgnoreHostKey(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
|
|
|||
144
vendor/golang.org/x/crypto/ssh/test/multi_auth_test.go
generated
vendored
Normal file
144
vendor/golang.org/x/crypto/ssh/test/multi_auth_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// 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.
|
||||
|
||||
// Tests for ssh client multi-auth
|
||||
//
|
||||
// These tests run a simple go ssh client against OpenSSH server
|
||||
// over unix domain sockets. The tests use multiple combinations
|
||||
// of password, keyboard-interactive and publickey authentication
|
||||
// methods.
|
||||
//
|
||||
// A wrapper library for making sshd PAM authentication use test
|
||||
// passwords is required in ./sshd_test_pw.so. If the library does
|
||||
// not exist these tests will be skipped. See compile instructions
|
||||
// (for linux) in file ./sshd_test_pw.c.
|
||||
|
||||
// +build linux
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// test cases
|
||||
type multiAuthTestCase struct {
|
||||
authMethods []string
|
||||
expectedPasswordCbs int
|
||||
expectedKbdIntCbs int
|
||||
}
|
||||
|
||||
// test context
|
||||
type multiAuthTestCtx struct {
|
||||
password string
|
||||
numPasswordCbs int
|
||||
numKbdIntCbs int
|
||||
}
|
||||
|
||||
// create test context
|
||||
func newMultiAuthTestCtx(t *testing.T) *multiAuthTestCtx {
|
||||
password, err := randomPassword()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate random test password: %s", err.Error())
|
||||
}
|
||||
|
||||
return &multiAuthTestCtx{
|
||||
password: password,
|
||||
}
|
||||
}
|
||||
|
||||
// password callback
|
||||
func (ctx *multiAuthTestCtx) passwordCb() (secret string, err error) {
|
||||
ctx.numPasswordCbs++
|
||||
return ctx.password, nil
|
||||
}
|
||||
|
||||
// keyboard-interactive callback
|
||||
func (ctx *multiAuthTestCtx) kbdIntCb(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
||||
if len(questions) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ctx.numKbdIntCbs++
|
||||
if len(questions) == 1 {
|
||||
return []string{ctx.password}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported keyboard-interactive flow")
|
||||
}
|
||||
|
||||
// TestMultiAuth runs several subtests for different combinations of password, keyboard-interactive and publickey authentication methods
|
||||
func TestMultiAuth(t *testing.T) {
|
||||
testCases := []multiAuthTestCase{
|
||||
// Test password,publickey authentication, assert that password callback is called 1 time
|
||||
multiAuthTestCase{
|
||||
authMethods: []string{"password", "publickey"},
|
||||
expectedPasswordCbs: 1,
|
||||
},
|
||||
// Test keyboard-interactive,publickey authentication, assert that keyboard-interactive callback is called 1 time
|
||||
multiAuthTestCase{
|
||||
authMethods: []string{"keyboard-interactive", "publickey"},
|
||||
expectedKbdIntCbs: 1,
|
||||
},
|
||||
// Test publickey,password authentication, assert that password callback is called 1 time
|
||||
multiAuthTestCase{
|
||||
authMethods: []string{"publickey", "password"},
|
||||
expectedPasswordCbs: 1,
|
||||
},
|
||||
// Test publickey,keyboard-interactive authentication, assert that keyboard-interactive callback is called 1 time
|
||||
multiAuthTestCase{
|
||||
authMethods: []string{"publickey", "keyboard-interactive"},
|
||||
expectedKbdIntCbs: 1,
|
||||
},
|
||||
// Test password,password authentication, assert that password callback is called 2 times
|
||||
multiAuthTestCase{
|
||||
authMethods: []string{"password", "password"},
|
||||
expectedPasswordCbs: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(strings.Join(testCase.authMethods, ","), func(t *testing.T) {
|
||||
ctx := newMultiAuthTestCtx(t)
|
||||
|
||||
server := newServerForConfig(t, "MultiAuth", map[string]string{"AuthMethods": strings.Join(testCase.authMethods, ",")})
|
||||
defer server.Shutdown()
|
||||
|
||||
clientConfig := clientConfig()
|
||||
server.setTestPassword(clientConfig.User, ctx.password)
|
||||
|
||||
publicKeyAuthMethod := clientConfig.Auth[0]
|
||||
clientConfig.Auth = nil
|
||||
for _, authMethod := range testCase.authMethods {
|
||||
switch authMethod {
|
||||
case "publickey":
|
||||
clientConfig.Auth = append(clientConfig.Auth, publicKeyAuthMethod)
|
||||
case "password":
|
||||
clientConfig.Auth = append(clientConfig.Auth,
|
||||
ssh.RetryableAuthMethod(ssh.PasswordCallback(ctx.passwordCb), 5))
|
||||
case "keyboard-interactive":
|
||||
clientConfig.Auth = append(clientConfig.Auth,
|
||||
ssh.RetryableAuthMethod(ssh.KeyboardInteractive(ctx.kbdIntCb), 5))
|
||||
default:
|
||||
t.Fatalf("Unknown authentication method %s", authMethod)
|
||||
}
|
||||
}
|
||||
|
||||
conn := server.Dial(clientConfig)
|
||||
defer conn.Close()
|
||||
|
||||
if ctx.numPasswordCbs != testCase.expectedPasswordCbs {
|
||||
t.Fatalf("passwordCallback was called %d times, expected %d times", ctx.numPasswordCbs, testCase.expectedPasswordCbs)
|
||||
}
|
||||
|
||||
if ctx.numKbdIntCbs != testCase.expectedKbdIntCbs {
|
||||
t.Fatalf("keyboardInteractiveCallback was called %d times, expected %d times", ctx.numKbdIntCbs, testCase.expectedKbdIntCbs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
173
vendor/golang.org/x/crypto/ssh/test/sshd_test_pw.c
generated
vendored
Normal file
173
vendor/golang.org/x/crypto/ssh/test/sshd_test_pw.c
generated
vendored
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
// 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.
|
||||
|
||||
// sshd_test_pw.c
|
||||
// Wrapper to inject test password data for sshd PAM authentication
|
||||
//
|
||||
// This wrapper implements custom versions of getpwnam, getpwnam_r,
|
||||
// getspnam and getspnam_r. These functions first call their real
|
||||
// libc versions, then check if the requested user matches test user
|
||||
// specified in env variable TEST_USER and if so replace the password
|
||||
// with crypted() value of TEST_PASSWD env variable.
|
||||
//
|
||||
// Compile:
|
||||
// gcc -Wall -shared -o sshd_test_pw.so -fPIC sshd_test_pw.c
|
||||
//
|
||||
// Compile with debug:
|
||||
// gcc -DVERBOSE -Wall -shared -o sshd_test_pw.so -fPIC sshd_test_pw.c
|
||||
//
|
||||
// Run sshd:
|
||||
// LD_PRELOAD="sshd_test_pw.so" TEST_USER="..." TEST_PASSWD="..." sshd ...
|
||||
|
||||
// +build ignore
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <pwd.h>
|
||||
#include <shadow.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef VERBOSE
|
||||
#define DEBUG(X...) fprintf(stderr, X)
|
||||
#else
|
||||
#define DEBUG(X...) while (0) { }
|
||||
#endif
|
||||
|
||||
/* crypt() password */
|
||||
static char *
|
||||
pwhash(char *passwd) {
|
||||
return strdup(crypt(passwd, "$6$"));
|
||||
}
|
||||
|
||||
/* Pointers to real functions in libc */
|
||||
static struct passwd * (*real_getpwnam)(const char *) = NULL;
|
||||
static int (*real_getpwnam_r)(const char *, struct passwd *, char *, size_t, struct passwd **) = NULL;
|
||||
static struct spwd * (*real_getspnam)(const char *) = NULL;
|
||||
static int (*real_getspnam_r)(const char *, struct spwd *, char *, size_t, struct spwd **) = NULL;
|
||||
|
||||
/* Cached test user and test password */
|
||||
static char *test_user = NULL;
|
||||
static char *test_passwd_hash = NULL;
|
||||
|
||||
static void
|
||||
init(void) {
|
||||
/* Fetch real libc function pointers */
|
||||
real_getpwnam = dlsym(RTLD_NEXT, "getpwnam");
|
||||
real_getpwnam_r = dlsym(RTLD_NEXT, "getpwnam_r");
|
||||
real_getspnam = dlsym(RTLD_NEXT, "getspnam");
|
||||
real_getspnam_r = dlsym(RTLD_NEXT, "getspnam_r");
|
||||
|
||||
/* abort if env variables are not defined */
|
||||
if (getenv("TEST_USER") == NULL || getenv("TEST_PASSWD") == NULL) {
|
||||
fprintf(stderr, "env variables TEST_USER and TEST_PASSWD are missing\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Fetch test user and test password from env */
|
||||
test_user = strdup(getenv("TEST_USER"));
|
||||
test_passwd_hash = pwhash(getenv("TEST_PASSWD"));
|
||||
|
||||
DEBUG("sshd_test_pw init():\n");
|
||||
DEBUG("\treal_getpwnam: %p\n", real_getpwnam);
|
||||
DEBUG("\treal_getpwnam_r: %p\n", real_getpwnam_r);
|
||||
DEBUG("\treal_getspnam: %p\n", real_getspnam);
|
||||
DEBUG("\treal_getspnam_r: %p\n", real_getspnam_r);
|
||||
DEBUG("\tTEST_USER: '%s'\n", test_user);
|
||||
DEBUG("\tTEST_PASSWD: '%s'\n", getenv("TEST_PASSWD"));
|
||||
DEBUG("\tTEST_PASSWD_HASH: '%s'\n", test_passwd_hash);
|
||||
}
|
||||
|
||||
static int
|
||||
is_test_user(const char *name) {
|
||||
if (test_user != NULL && strcmp(test_user, name) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* getpwnam */
|
||||
|
||||
struct passwd *
|
||||
getpwnam(const char *name) {
|
||||
struct passwd *pw;
|
||||
|
||||
DEBUG("sshd_test_pw getpwnam(%s)\n", name);
|
||||
|
||||
if (real_getpwnam == NULL)
|
||||
init();
|
||||
if ((pw = real_getpwnam(name)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (is_test_user(name))
|
||||
pw->pw_passwd = strdup(test_passwd_hash);
|
||||
|
||||
return pw;
|
||||
}
|
||||
|
||||
/* getpwnam_r */
|
||||
|
||||
int
|
||||
getpwnam_r(const char *name,
|
||||
struct passwd *pwd,
|
||||
char *buf,
|
||||
size_t buflen,
|
||||
struct passwd **result) {
|
||||
int r;
|
||||
|
||||
DEBUG("sshd_test_pw getpwnam_r(%s)\n", name);
|
||||
|
||||
if (real_getpwnam_r == NULL)
|
||||
init();
|
||||
if ((r = real_getpwnam_r(name, pwd, buf, buflen, result)) != 0 || *result == NULL)
|
||||
return r;
|
||||
|
||||
if (is_test_user(name))
|
||||
pwd->pw_passwd = strdup(test_passwd_hash);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* getspnam */
|
||||
|
||||
struct spwd *
|
||||
getspnam(const char *name) {
|
||||
struct spwd *sp;
|
||||
|
||||
DEBUG("sshd_test_pw getspnam(%s)\n", name);
|
||||
|
||||
if (real_getspnam == NULL)
|
||||
init();
|
||||
if ((sp = real_getspnam(name)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (is_test_user(name))
|
||||
sp->sp_pwdp = strdup(test_passwd_hash);
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
/* getspnam_r */
|
||||
|
||||
int
|
||||
getspnam_r(const char *name,
|
||||
struct spwd *spbuf,
|
||||
char *buf,
|
||||
size_t buflen,
|
||||
struct spwd **spbufp) {
|
||||
int r;
|
||||
|
||||
DEBUG("sshd_test_pw getspnam_r(%s)\n", name);
|
||||
|
||||
if (real_getspnam_r == NULL)
|
||||
init();
|
||||
if ((r = real_getspnam_r(name, spbuf, buf, buflen, spbufp)) != 0)
|
||||
return r;
|
||||
|
||||
if (is_test_user(name))
|
||||
spbuf->sp_pwdp = strdup(test_passwd_hash);
|
||||
|
||||
return r;
|
||||
}
|
||||
73
vendor/golang.org/x/crypto/ssh/test/test_unix_test.go
generated
vendored
73
vendor/golang.org/x/crypto/ssh/test/test_unix_test.go
generated
vendored
|
|
@ -10,6 +10,8 @@ package test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
|
@ -25,7 +27,8 @@ import (
|
|||
"golang.org/x/crypto/ssh/testdata"
|
||||
)
|
||||
|
||||
const sshdConfig = `
|
||||
const (
|
||||
defaultSshdConfig = `
|
||||
Protocol 2
|
||||
Banner {{.Dir}}/banner
|
||||
HostKey {{.Dir}}/id_rsa
|
||||
|
|
@ -50,8 +53,17 @@ RhostsRSAAuthentication no
|
|||
HostbasedAuthentication no
|
||||
PubkeyAcceptedKeyTypes=*
|
||||
`
|
||||
multiAuthSshdConfigTail = `
|
||||
UsePAM yes
|
||||
PasswordAuthentication yes
|
||||
ChallengeResponseAuthentication yes
|
||||
AuthenticationMethods {{.AuthMethods}}
|
||||
`
|
||||
)
|
||||
|
||||
var configTmpl = template.Must(template.New("").Parse(sshdConfig))
|
||||
var configTmpl = map[string]*template.Template{
|
||||
"default": template.Must(template.New("").Parse(defaultSshdConfig)),
|
||||
"MultiAuth": template.Must(template.New("").Parse(defaultSshdConfig + multiAuthSshdConfigTail))}
|
||||
|
||||
type server struct {
|
||||
t *testing.T
|
||||
|
|
@ -60,6 +72,10 @@ type server struct {
|
|||
cmd *exec.Cmd
|
||||
output bytes.Buffer // holds stderr from sshd process
|
||||
|
||||
testUser string // test username for sshd
|
||||
testPasswd string // test password for sshd
|
||||
sshdTestPwSo string // dynamic library to inject a custom password into sshd
|
||||
|
||||
// Client half of the network connection.
|
||||
clientConn net.Conn
|
||||
}
|
||||
|
|
@ -186,6 +202,20 @@ func (s *server) TryDialWithAddr(config *ssh.ClientConfig, addr string) (*ssh.Cl
|
|||
s.cmd.Stdin = f
|
||||
s.cmd.Stdout = f
|
||||
s.cmd.Stderr = &s.output
|
||||
|
||||
if s.sshdTestPwSo != "" {
|
||||
if s.testUser == "" {
|
||||
s.t.Fatal("user missing from sshd_test_pw.so config")
|
||||
}
|
||||
if s.testPasswd == "" {
|
||||
s.t.Fatal("password missing from sshd_test_pw.so config")
|
||||
}
|
||||
s.cmd.Env = append(os.Environ(),
|
||||
fmt.Sprintf("LD_PRELOAD=%s", s.sshdTestPwSo),
|
||||
fmt.Sprintf("TEST_USER=%s", s.testUser),
|
||||
fmt.Sprintf("TEST_PASSWD=%s", s.testPasswd))
|
||||
}
|
||||
|
||||
if err := s.cmd.Start(); err != nil {
|
||||
s.t.Fail()
|
||||
s.Shutdown()
|
||||
|
|
@ -236,8 +266,39 @@ func writeFile(path string, contents []byte) {
|
|||
}
|
||||
}
|
||||
|
||||
// generate random password
|
||||
func randomPassword() (string, error) {
|
||||
b := make([]byte, 12)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
// setTestPassword is used for setting user and password data for sshd_test_pw.so
|
||||
// This function also checks that ./sshd_test_pw.so exists and if not calls s.t.Skip()
|
||||
func (s *server) setTestPassword(user, passwd string) error {
|
||||
wd, _ := os.Getwd()
|
||||
wrapper := filepath.Join(wd, "sshd_test_pw.so")
|
||||
if _, err := os.Stat(wrapper); err != nil {
|
||||
s.t.Skip(fmt.Errorf("sshd_test_pw.so is not available"))
|
||||
return err
|
||||
}
|
||||
|
||||
s.sshdTestPwSo = wrapper
|
||||
s.testUser = user
|
||||
s.testPasswd = passwd
|
||||
return nil
|
||||
}
|
||||
|
||||
// newServer returns a new mock ssh server.
|
||||
func newServer(t *testing.T) *server {
|
||||
return newServerForConfig(t, "default", map[string]string{})
|
||||
}
|
||||
|
||||
// newServerForConfig returns a new mock ssh server.
|
||||
func newServerForConfig(t *testing.T, config string, configVars map[string]string) *server {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test due to -short")
|
||||
}
|
||||
|
|
@ -249,9 +310,11 @@ func newServer(t *testing.T) *server {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = configTmpl.Execute(f, map[string]string{
|
||||
"Dir": dir,
|
||||
})
|
||||
if _, ok := configTmpl[config]; ok == false {
|
||||
t.Fatal(fmt.Errorf("Invalid server config '%s'", config))
|
||||
}
|
||||
configVars["Dir"] = dir
|
||||
err = configTmpl[config].Execute(f, configVars)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue