From dd5a38bd8a09d5dfb0d1af09c48218d1077abe7c Mon Sep 17 00:00:00 2001 From: Boshi Lian Date: Sun, 25 Feb 2018 04:06:38 +0800 Subject: [PATCH] update sshpiper lib --- Gopkg.lock | 2 +- sshpiperd/sshpiperd.go | 4 +- sshpiperd/upstream/mysql/workingdir.go | 6 +- sshpiperd/upstream/provider.go | 2 +- sshpiperd/upstream/workingdir/workingdir.go | 4 +- .../x/crypto/ssh/client_auth_test.go | 4 +- vendor/golang.org/x/crypto/ssh/server.go | 16 +- vendor/golang.org/x/crypto/ssh/sshpiper.go | 102 ++++---- .../golang.org/x/crypto/ssh/sshpiper_test.go | 223 ++++++++++++++---- 9 files changed, 244 insertions(+), 119 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index bed2b21a..5f3a7a4d 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -40,7 +40,7 @@ "ssh", "ssh/testdata" ] - revision = "c832b236b4049eae471365acd18bc0b462a64a2b" + revision = "a3947c90260b2290d5db1b779520e75f21ad8baf" source = "https://github.com/tg123/sshpiper.crypto" [solve-meta] diff --git a/sshpiperd/sshpiperd.go b/sshpiperd/sshpiperd.go index 58505856..d474d3d2 100644 --- a/sshpiperd/sshpiperd.go +++ b/sshpiperd/sshpiperd.go @@ -42,7 +42,7 @@ func getAndInstall(name string, get func(n string) registry.Plugin, install func return install(p) } -func installDrivers(piper *ssh.SSHPiperConfig, config *piperdConfig, logger *log.Logger) (auditor.Provider, error) { +func installDrivers(piper *ssh.PiperConfig, config *piperdConfig, logger *log.Logger) (auditor.Provider, error) { // install upstreamProvider driver if config.UpstreamDriver == "" { @@ -115,7 +115,7 @@ func startPiper(config *piperdConfig, logger *log.Logger) error { logger.Println("sshpiper is about to start") - piper := &ssh.SSHPiperConfig{} + piper := &ssh.PiperConfig{} bigbro, err := installDrivers(piper, config, logger) diff --git a/sshpiperd/upstream/mysql/workingdir.go b/sshpiperd/upstream/mysql/workingdir.go index 7d124711..4c632c2e 100644 --- a/sshpiperd/upstream/mysql/workingdir.go +++ b/sshpiperd/upstream/mysql/workingdir.go @@ -39,7 +39,7 @@ func connectServer(db *sql.DB, sid int64) (net.Conn, error) { return net.Dial("tcp", addr) } -func (w *mysqlWorkingDir) connectUpstream(db *sql.DB, uid int64, defuser string) (net.Conn, *ssh.SSHPiperAuthPipe, error) { +func (w *mysqlWorkingDir) connectUpstream(db *sql.DB, uid int64, defuser string) (net.Conn, *ssh.AuthPipe, error) { o := crud.NewUpstream(db) @@ -64,7 +64,7 @@ func (w *mysqlWorkingDir) connectUpstream(db *sql.DB, uid int64, defuser string) } logger.Printf("connecting upstream id [%v] addr [%v]@[%v] ", uid, user, c.RemoteAddr()) - return c, &ssh.SSHPiperAuthPipe{ + return c, &ssh.AuthPipe{ User: user, PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (ssh.AuthPipeType, ssh.AuthMethod, error) { @@ -134,7 +134,7 @@ func findByUsername(db *sql.DB, username string) (int64, error) { } //func (w *mysqlWorkingDir) FindUpstream(conn ssh.ConnMetadata, downkey ssh.PublicKey) (net.Conn, *ssh.SSHPiperAuthPipe, error) { -func (w *mysqlWorkingDir) FindUpstream(conn ssh.ConnMetadata) (net.Conn, *ssh.SSHPiperAuthPipe, error) { +func (w *mysqlWorkingDir) FindUpstream(conn ssh.ConnMetadata) (net.Conn, *ssh.AuthPipe, error) { db, err := w.ConnectDB() defer db.Close() diff --git a/sshpiperd/upstream/provider.go b/sshpiperd/upstream/provider.go index 6d8d7e32..f07945e4 100644 --- a/sshpiperd/upstream/provider.go +++ b/sshpiperd/upstream/provider.go @@ -12,7 +12,7 @@ import ( // the returned auth pipe is to map/convert downstream auth method to another auth for // connecting to upstream. // e.g. map downstream public key to another upstream private key -type Handler func(conn ssh.ConnMetadata) (net.Conn, *ssh.SSHPiperAuthPipe, error) +type Handler func(conn ssh.ConnMetadata) (net.Conn, *ssh.AuthPipe, error) // Provider is a factory for Upstream Provider type Provider interface { diff --git a/sshpiperd/upstream/workingdir/workingdir.go b/sshpiperd/upstream/workingdir/workingdir.go index dcad81d2..accf7c4d 100644 --- a/sshpiperd/upstream/workingdir/workingdir.go +++ b/sshpiperd/upstream/workingdir/workingdir.go @@ -122,7 +122,7 @@ func parseUpstreamFile(data string) (string, string) { return line, user } -func findUpstreamFromUserfile(conn ssh.ConnMetadata) (net.Conn, *ssh.SSHPiperAuthPipe, error) { +func findUpstreamFromUserfile(conn ssh.ConnMetadata) (net.Conn, *ssh.AuthPipe, error) { user := conn.User() if !checkUsername(user) { @@ -152,7 +152,7 @@ func findUpstreamFromUserfile(conn ssh.ConnMetadata) (net.Conn, *ssh.SSHPiperAut return nil, nil, err } - return c, &ssh.SSHPiperAuthPipe{ + return c, &ssh.AuthPipe{ User: mappedUser, PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (ssh.AuthPipeType, ssh.AuthMethod, error) { diff --git a/vendor/golang.org/x/crypto/ssh/client_auth_test.go b/vendor/golang.org/x/crypto/ssh/client_auth_test.go index a6b8b999..e457ca53 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth_test.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth_test.go @@ -614,8 +614,8 @@ func TestClientAuthErrorList(t *testing.T) { for i, e := range authErrs.Errors { switch i { case 0: - if _, ok := e.(*NoAuthError); !ok { - t.Fatalf("errors: got error type %T, want NoAuthError", e) + if e != NoAuthError { + t.Fatalf("errors: got error %v, want NoAuthError", e) } case 1: if e != publicKeyErr { diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index f40657d3..6262f341 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -297,7 +297,7 @@ func checkSourceAddress(addr net.Addr, sourceAddrs string) error { // provided by the user failed to authenticate. type ServerAuthError struct { // Errors contains authentication errors returned by the authentication - // callback methods. + // callback methods. The first entry typically is NoAuthError. Errors []error } @@ -309,13 +309,11 @@ func (l ServerAuthError) Error() string { return "[" + strings.Join(errs, ", ") + "]" } -// NoAuthError is the unique error that is returned if no authentication method -// has been passed yet -type NoAuthError struct{} - -func (e *NoAuthError) Error() string { - return "no auth passed yet" -} +// NoAuthError is the unique error that is returned if no +// authentication method has been passed yet. This happens as a normal +// part of the authentication loop, since the client first tries +// 'none' authentication to discover available methods. +var NoAuthError = errors.New("ssh: no auth passed yet") func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { sessionID := s.transport.getSessionID() @@ -371,7 +369,7 @@ userAuthLoop: } perms = nil - authErr := error(&NoAuthError{}) + authErr := NoAuthError switch userAuthReq.Method { case "none": diff --git a/vendor/golang.org/x/crypto/ssh/sshpiper.go b/vendor/golang.org/x/crypto/ssh/sshpiper.go index 95385357..19dee484 100644 --- a/vendor/golang.org/x/crypto/ssh/sshpiper.go +++ b/vendor/golang.org/x/crypto/ssh/sshpiper.go @@ -11,54 +11,52 @@ import ( "net" ) +// AuthPipeType declares how sshpiper handle piped auth message type AuthPipeType int const ( - // Do nothing but pass auth message to upstream + // AuthPipeTypePassThrough does nothing but pass auth message to upstream AuthPipeTypePassThrough AuthPipeType = iota - // Convert auth message to AuthMetod return by callback and pass it to upstream + // AuthPipeTypeMap converts auth message to AuthMetod return by callback and pass it to upstream AuthPipeTypeMap - // Discard auth message, do not pass it to uptream + // AuthPipeTypeDiscard discards auth message, do not pass it to uptream AuthPipeTypeDiscard - // Convert auth message to NoneAuth and pass it to upstream + // AuthPipeTypeNone converts auth message to NoneAuth and pass it to upstream AuthPipeTypeNone ) -// SSHPiperAuthPipe: -// Convert Auth -// Any Auth to Password +// AuthPipe contains the callbacks of auth msg mapping from downstream to upstream // -// Any Auth to Public Key +// when AuthPipeType == AuthPipeTypeMap && AuthMethod == PublicKey // SSHPiper will sign the auth packet message using the returned Signer. // This func might be called twice, one is for query message, the other // is real auth packet message. // If any error occurs during this period, a NoneAuth packet will be sent to // upstream ssh server instead. -// // More info: https://github.com/tg123/sshpiper#publickey-sign-again -type SSHPiperAuthPipe struct { +type AuthPipe struct { // Username to upstream User string + // PublicKeyCallback, if non-nil, is called when downstream requests a password auth. PasswordCallback func(conn ConnMetadata, password []byte) (AuthPipeType, AuthMethod, error) // PublicKeyCallback, if non-nil, is called when downstream requests a publickey auth. PublicKeyCallback func(conn ConnMetadata, key PublicKey) (AuthPipeType, AuthMethod, error) - // HostKeyCallback is called during the cryptographic + // UpstreamHostKeyCallback 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 { +// PiperConfig holds SSHPiper specific configuration data. +type PiperConfig struct { Config hostKeys []Signer @@ -74,7 +72,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, *SSHPiperAuthPipe, error) + FindUpstream func(conn ConnMetadata) (net.Conn, *AuthPipe, error) // ServerVersion is the version identification string to announce in // the public handshake. @@ -97,11 +95,11 @@ type pipedConn struct { hookDownstreamMsg func(msg []byte) ([]byte, error) } -// SSHPiperConn is a piped SSH connection, linking upstream ssh server and +// PiperConn is a piped SSH connection, linking upstream ssh server and // downstream ssh client together. After the piped connection was created, // The downstream ssh client is authenticated by upstream ssh server and // AdditionalChallenge from SSHPiper. -type SSHPiperConn struct { +type PiperConn struct { *pipedConn HookUpstreamMsg func(conn ConnMetadata, msg []byte) ([]byte, error) @@ -110,7 +108,7 @@ type SSHPiperConn struct { // Wait blocks until the piped connection has shut down, and returns the // error causing the shutdown. -func (p *SSHPiperConn) Wait() error { +func (p *PiperConn) Wait() error { p.pipedConn.hookUpstreamMsg = func(msg []byte) ([]byte, error) { if p.HookUpstreamMsg != nil { @@ -133,22 +131,24 @@ func (p *SSHPiperConn) Wait() error { } // Close the piped connection create by SSHPiper -func (p *SSHPiperConn) Close() { +func (p *PiperConn) Close() { p.pipedConn.Close() } -func (p *SSHPiperConn) UpstreamConnMeta() ConnMetadata { +// UpstreamConnMeta returns the ConnMetadata of the piper and upstream +func (p *PiperConn) UpstreamConnMeta() ConnMetadata { return p.pipedConn.upstream } -func (p *SSHPiperConn) DownstreamConnMeta() ConnMetadata { +// DownstreamConnMeta returns the ConnMetadata of the piper and downstream +func (p *PiperConn) DownstreamConnMeta() ConnMetadata { return p.pipedConn.downstream } // AddHostKey adds a private key as a SSHPiper host key. If an existing host // key exists with the same algorithm, it is overwritten. Each SSHPiper // config must have at least one host key. -func (s *SSHPiperConfig) AddHostKey(key Signer) { +func (s *PiperConfig) AddHostKey(key Signer) { for i, k := range s.hostKeys { if k.PublicKey().Type() == key.PublicKey().Type() { s.hostKeys[i] = key @@ -162,7 +162,7 @@ func (s *SSHPiperConfig) AddHostKey(key Signer) { // NewSSHPiperConn starts a piped ssh connection witch conn as its downstream transport. // It handshake with downstream ssh client and upstream ssh server provicde by FindUpstream. // If either handshake is unsuccessful, the whole piped connection will be closed. -func NewSSHPiperConn(conn net.Conn, piper *SSHPiperConfig) (pipe *SSHPiperConn, err error) { +func NewSSHPiperConn(conn net.Conn, piper *PiperConfig) (pipe *PiperConn, err error) { if piper.FindUpstream == nil { return nil, errors.New("sshpiper: must specify FindUpstream") @@ -251,6 +251,7 @@ func NewSSHPiperConn(conn net.Conn, piper *SSHPiperConfig) (pipe *SSHPiperConn, u.Close() } }() + u.user = mappedUser p := &pipedConn{ upstream: u, @@ -259,7 +260,7 @@ func NewSSHPiperConn(conn net.Conn, piper *SSHPiperConfig) (pipe *SSHPiperConn, p.processAuthMsg = func(msg *userAuthRequestMsg) (*userAuthRequestMsg, error) { - var authType AuthPipeType = AuthPipeTypePassThrough + var authType = AuthPipeTypePassThrough var authMethod AuthMethod switch msg.Method { @@ -290,18 +291,16 @@ func NewSSHPiperConn(conn net.Conn, piper *SSHPiperConfig) (pipe *SSHPiperConn, // discard msg return nil, nil - } else { + } - ok, err := p.checkPublicKey(msg, downKey, sig) + ok, err := p.checkPublicKey(msg, downKey, sig) - if err != nil { - return nil, err - } - - if !ok { - return noneAuthMsg(mappedUser), nil - } + if err != nil { + return nil, err + } + if !ok { + return noneAuthMsg(mappedUser), nil } case "password": @@ -406,7 +405,7 @@ func NewSSHPiperConn(conn net.Conn, piper *SSHPiperConfig) (pipe *SSHPiperConn, return nil, err } - return &SSHPiperConn{pipedConn: p}, nil + return &PiperConn{pipedConn: p}, nil } func (pipe *pipedConn) ack(key PublicKey) error { @@ -415,11 +414,7 @@ func (pipe *pipedConn) ack(key PublicKey) error { PubKey: key.Marshal(), } - if err := pipe.downstream.transport.writePacket(Marshal(&okMsg)); err != nil { - return err - } - - return nil + return pipe.downstream.transport.writePacket(Marshal(&okMsg)) } // not used after method to method map enable @@ -638,7 +633,6 @@ func (pipe *pipedConn) pipeAuth(initUserAuthMsg *userAuthRequestMsg) error { if succ { return nil } - } var packet []byte @@ -688,11 +682,7 @@ func (u *upstream) sendAuthReq() error { return err } var serviceAccept serviceAcceptMsg - if err := Unmarshal(packet, &serviceAccept); err != nil { - return err - } - - return nil + return Unmarshal(packet, &serviceAccept) } func newDownstream(c net.Conn, config *ServerConfig) (*downstream, error) { @@ -770,41 +760,39 @@ func (c *connection) clientHandshakeNoAuth(dialAddress string, config *ClientCon if err := c.transport.waitSession(); err != nil { return err - } c.sessionID = c.transport.getSessionID() - return nil } -func (s *connection) serverHandshakeNoAuth(config *ServerConfig) (*Permissions, error) { +func (c *connection) serverHandshakeNoAuth(config *ServerConfig) (*Permissions, error) { if len(config.hostKeys) == 0 { return nil, errors.New("ssh: server has no host keys") } var err error if config.ServerVersion != "" { - s.serverVersion = []byte(config.ServerVersion) + c.serverVersion = []byte(config.ServerVersion) } else { - s.serverVersion = []byte("SSH-2.0-SSHPiper") + c.serverVersion = []byte("SSH-2.0-SSHPiper") } - s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion) + c.clientVersion, err = exchangeVersions(c.sshConn.conn, c.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) + tr := newTransport(c.sshConn.conn, config.Rand, false /* not client */) + c.transport = newServerTransport(tr, c.clientVersion, c.serverVersion, config) - if err := s.transport.waitSession(); err != nil { + if err := c.transport.waitSession(); err != nil { return nil, err } - s.sessionID = s.transport.getSessionID() + c.sessionID = c.transport.getSessionID() var packet []byte - if packet, err = s.transport.readPacket(); err != nil { + if packet, err = c.transport.readPacket(); err != nil { return nil, err } @@ -818,7 +806,7 @@ func (s *connection) serverHandshakeNoAuth(config *ServerConfig) (*Permissions, serviceAccept := serviceAcceptMsg{ Service: serviceUserAuth, } - if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil { + if err := c.transport.writePacket(Marshal(&serviceAccept)); err != nil { return nil, err } diff --git a/vendor/golang.org/x/crypto/ssh/sshpiper_test.go b/vendor/golang.org/x/crypto/ssh/sshpiper_test.go index 2442b976..6ac9bce1 100644 --- a/vendor/golang.org/x/crypto/ssh/sshpiper_test.go +++ b/vendor/golang.org/x/crypto/ssh/sshpiper_test.go @@ -20,16 +20,16 @@ func ExampleNewSSHPiperConn() { // upstream addr const serverAddr = "127.0.0.1:22" - piper := &SSHPiperConfig{ + piper := &PiperConfig{ // return conn dial to serverAddr - FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) { + FindUpstream: func(conn ConnMetadata) (net.Conn, *AuthPipe, error) { c, err := net.Dial("tcp", serverAddr) if err != nil { return nil, nil, err } // change upstream username to root - return c, &SSHPiperAuthPipe{ + return c, &AuthPipe{ User: "root", UpstreamHostKeyCallback: InsecureIgnoreHostKey(), }, nil @@ -71,7 +71,7 @@ func ExampleNewSSHPiperConn() { // }}} -func dialPiper(piper *SSHPiperConfig) (net.Conn, error) { +func dialPiper(piper *PiperConfig, afterConn func(*PiperConn), t *testing.T) (net.Conn, error) { c, s, err := netPipe() if err != nil { return nil, err @@ -86,10 +86,14 @@ func dialPiper(piper *SSHPiperConfig) (net.Conn, error) { p, err := NewSSHPiperConn(s, piper) if err != nil { - fmt.Println(err) + t.Errorf("failed to create piper conn %v", err) return } + if afterConn != nil { + afterConn(p) + } + p.Wait() }() @@ -102,8 +106,8 @@ func TestPiperFindUpstreamCallback(t *testing.T) { var called bool - c, err := dialPiper(&SSHPiperConfig{ - FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) { + c, err := dialPiper(&PiperConfig{ + FindUpstream: func(conn ConnMetadata) (net.Conn, *AuthPipe, error) { if username != conn.User() { t.Errorf("different username") } @@ -124,11 +128,11 @@ func TestPiperFindUpstreamCallback(t *testing.T) { }, }, t) - return s, &SSHPiperAuthPipe{ + return s, &AuthPipe{ UpstreamHostKeyCallback: InsecureIgnoreHostKey(), }, err }, - }) + }, nil, t) if err != nil { t.Fatalf("connect dial to piper: %v", err) @@ -153,8 +157,8 @@ func TestPiperFindUpstreamWithUserCallback(t *testing.T) { var called bool - c, err := dialPiper(&SSHPiperConfig{ - FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) { + c, err := dialPiper(&PiperConfig{ + FindUpstream: func(conn ConnMetadata) (net.Conn, *AuthPipe, error) { s, err := dialUpstream(simpleEchoHandler, &ServerConfig{ PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) { @@ -168,12 +172,12 @@ func TestPiperFindUpstreamWithUserCallback(t *testing.T) { }, }, t) - return s, &SSHPiperAuthPipe{ + return s, &AuthPipe{ User: mappedname, UpstreamHostKeyCallback: InsecureIgnoreHostKey(), }, err }, - }) + }, nil, t) if err != nil { t.Fatalf("connect dial to piper: %v", err) @@ -208,12 +212,12 @@ func TestPiperMapPublicKey(t *testing.T) { }, } - c, err := dialPiper(&SSHPiperConfig{ - FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) { + c, err := dialPiper(&PiperConfig{ + FindUpstream: func(conn ConnMetadata) (net.Conn, *AuthPipe, error) { s, err := dialUpstream(simpleEchoHandler, &ServerConfig{ PublicKeyCallback: certChecker.Authenticate, }, t) - return s, &SSHPiperAuthPipe{ + return s, &AuthPipe{ PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (AuthPipeType, AuthMethod, error) { return AuthPipeTypeMap, PublicKeys(testSigners["rsa"]), nil @@ -222,7 +226,7 @@ func TestPiperMapPublicKey(t *testing.T) { UpstreamHostKeyCallback: InsecureIgnoreHostKey(), }, err }, - }) + }, nil, t) if err != nil { t.Fatalf("connect dial to piper: %v", err) @@ -260,8 +264,8 @@ func TestPiperMapPublicKeyToPassword(t *testing.T) { var called bool - c, err := dialPiper(&SSHPiperConfig{ - FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) { + c, err := dialPiper(&PiperConfig{ + FindUpstream: func(conn ConnMetadata) (net.Conn, *AuthPipe, error) { s, err := dialUpstream(simpleEchoHandler, &ServerConfig{ PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) { t.Errorf("PasswordCallback should not be called") @@ -273,7 +277,7 @@ func TestPiperMapPublicKeyToPassword(t *testing.T) { return certChecker.Authenticate(conn, key) }, }, t) - return s, &SSHPiperAuthPipe{ + return s, &AuthPipe{ PasswordCallback: func(conn ConnMetadata, password []byte) (AuthPipeType, AuthMethod, error) { if string(password) != "mypassword" { t.Errorf("password not equal") @@ -285,7 +289,7 @@ func TestPiperMapPublicKeyToPassword(t *testing.T) { UpstreamHostKeyCallback: InsecureIgnoreHostKey(), }, err }, - }) + }, nil, t) if err != nil { t.Fatalf("connect dial to piper: %v", err) @@ -311,8 +315,8 @@ func TestPiperMapPublicKeyToPassword(t *testing.T) { func TestPiperPasswordToMapPublicKey(t *testing.T) { var called bool - c, err := dialPiper(&SSHPiperConfig{ - FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) { + c, err := dialPiper(&PiperConfig{ + FindUpstream: func(conn ConnMetadata) (net.Conn, *AuthPipe, error) { s, err := dialUpstream(simpleEchoHandler, &ServerConfig{ PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) { called = true @@ -329,7 +333,7 @@ func TestPiperPasswordToMapPublicKey(t *testing.T) { return nil, nil }, }, t) - return s, &SSHPiperAuthPipe{ + return s, &AuthPipe{ PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (AuthPipeType, AuthMethod, error) { return AuthPipeTypeMap, Password("mypassword"), nil @@ -338,7 +342,7 @@ func TestPiperPasswordToMapPublicKey(t *testing.T) { UpstreamHostKeyCallback: InsecureIgnoreHostKey(), }, err }, - }) + }, nil, t) if err != nil { t.Fatalf("connect dial to piper: %v", err) @@ -368,8 +372,8 @@ func TestPiperServerWithBanner(t *testing.T) { var called bool - c, err := dialPiper(&SSHPiperConfig{ - FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) { + c, err := dialPiper(&PiperConfig{ + FindUpstream: func(conn ConnMetadata) (net.Conn, *AuthPipe, error) { if username != conn.User() { t.Errorf("different username") } @@ -387,12 +391,12 @@ func TestPiperServerWithBanner(t *testing.T) { }, }, t) - return s, &SSHPiperAuthPipe{ + return s, &AuthPipe{ User: mappedname, UpstreamHostKeyCallback: InsecureIgnoreHostKey(), }, err }, - }) + }, nil, t) if err != nil { t.Fatalf("connect dial to piper: %v", err) @@ -437,8 +441,8 @@ func TestPiperUsernameNotChangedWithinSession(t *testing.T) { }, } - c, err := dialPiper(&SSHPiperConfig{ - FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) { + c, err := dialPiper(&PiperConfig{ + FindUpstream: func(conn ConnMetadata) (net.Conn, *AuthPipe, error) { s, err := dialUpstream(simpleEchoHandler, &ServerConfig{ PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) { if conn.User() != mappedname { @@ -459,10 +463,10 @@ func TestPiperUsernameNotChangedWithinSession(t *testing.T) { t.Errorf("bad mapped username") } - callcount += 1 + callcount++ }, }, t) - return s, &SSHPiperAuthPipe{ + return s, &AuthPipe{ User: mappedname, PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (AuthPipeType, AuthMethod, error) { @@ -472,7 +476,7 @@ func TestPiperUsernameNotChangedWithinSession(t *testing.T) { UpstreamHostKeyCallback: InsecureIgnoreHostKey(), }, err }, - }) + }, nil, t) if err != nil { t.Fatalf("connect dial to piper: %v", err) @@ -498,7 +502,7 @@ func TestPiperUsernameNotChangedWithinSession(t *testing.T) { } func TestPiperAdditionalChallenge(t *testing.T) { - c, err := dialPiper(&SSHPiperConfig{ + c, err := dialPiper(&PiperConfig{ AdditionalChallenge: func(conn ConnMetadata, challenge KeyboardInteractiveChallenge) (bool, error) { ans, err := challenge("user", "instruction", @@ -516,13 +520,13 @@ func TestPiperAdditionalChallenge(t *testing.T) { } return false, fmt.Errorf("keyboard-interactive failed") }, - FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) { + FindUpstream: func(conn ConnMetadata) (net.Conn, *AuthPipe, error) { s, err := dialUpstream(simpleEchoHandler, &ServerConfig{NoClientAuth: true}, t) - return s, &SSHPiperAuthPipe{ + return s, &AuthPipe{ UpstreamHostKeyCallback: InsecureIgnoreHostKey(), }, err }, - }) + }, nil, t) if err != nil { t.Fatalf("connect dial to piper: %v", err) @@ -588,17 +592,152 @@ func dialUpstream(handler serverType, upstream *ServerConfig, t *testing.T) (net return c, nil } -func TestPiperPipeData(t *testing.T) { +func TestPiperConnMeta(t *testing.T) { - c, err := dialPiper(&SSHPiperConfig{ - FindUpstream: func(conn ConnMetadata) (net.Conn, *SSHPiperAuthPipe, error) { + wait := make(chan int) + + c, err := dialPiper(&PiperConfig{ + FindUpstream: func(conn ConnMetadata) (net.Conn, *AuthPipe, error) { s, err := dialUpstream(simpleEchoHandler, &ServerConfig{NoClientAuth: true}, t) - return s, &SSHPiperAuthPipe{ + return s, &AuthPipe{ + User: "up", UpstreamHostKeyCallback: InsecureIgnoreHostKey(), }, err }, + }, func(p *PiperConn) { + + if p.DownstreamConnMeta().User() != "down" { + t.Errorf("different downstream user") + } + + if p.UpstreamConnMeta().User() != "up" { + t.Errorf("different upstream user") + } + + wait <- 0 + }, t) + + _, _, _, err = NewClientConn(c, "", &ClientConfig{ + User: "down", + Auth: []AuthMethod{new(noneAuth)}, + HostKeyCallback: InsecureIgnoreHostKey(), }) + if err != nil { + t.Fatalf("can connect to piper %v", err) + } + + <-wait +} + +func TestPiperConnMsgHook(t *testing.T) { + + wait := make(chan int) + + c, err := dialPiper(&PiperConfig{ + FindUpstream: func(conn ConnMetadata) (net.Conn, *AuthPipe, error) { + s, err := dialUpstream(simpleEchoHandler, &ServerConfig{NoClientAuth: true}, t) + return s, &AuthPipe{ + UpstreamHostKeyCallback: InsecureIgnoreHostKey(), + }, err + }, + }, func(p *PiperConn) { + + p.HookDownstreamMsg = func(conn ConnMetadata, msg []byte) ([]byte, error) { + if msg[0] == msgChannelData { + m := channelDataMsg{} + Unmarshal(msg, &m) + if string(m.Rest) != "123456" { + t.Errorf("msg not equal") + } + + m.Length = 3 + m.Rest = []byte("654") + + return Marshal(m), nil + } + + return msg, nil + } + + p.HookUpstreamMsg = func(conn ConnMetadata, msg []byte) ([]byte, error) { + if msg[0] == msgChannelData { + m := channelDataMsg{} + Unmarshal(msg, &m) + if string(m.Rest) != "654" { + t.Errorf("msg not equal") + } + + m.Length = 7 + m.Rest = []byte("abcdefg") + + return Marshal(m), nil + } + + return msg, nil + } + + wait <- 0 + }, t) + + sshc, chans, reqs, err := NewClientConn(c, "", &ClientConfig{ + User: "test", + Auth: []AuthMethod{new(noneAuth)}, + HostKeyCallback: InsecureIgnoreHostKey(), + }) + + if err != nil { + t.Fatalf("can connect to piper %v", err) + } + <-wait + + conn := NewClient(sshc, chans, reqs) + defer conn.Close() + + session, err := conn.NewSession() + if err != nil { + t.Fatal(err) + } + defer session.Close() + + stdin, err := session.StdinPipe() + if err != nil { + t.Fatalf("StdinPipe failed: %v", err) + } + stdout, err := session.StdoutPipe() + if err != nil { + t.Fatalf("StdoutPipe failed: %v", err) + } + + data := []byte(`123456`) + _, err = stdin.Write(data) + if err != nil { + t.Fatalf("Write failed: %v", err) + } + stdin.Close() + + res, err := ioutil.ReadAll(stdout) + if err != nil { + t.Fatalf("Read failed: %v", err) + } + + if !bytes.Equal([]byte(`abcdefg`), res) { + t.Fatalf("Read differed from write, wrote: %v, read: %v", data, res) + } + +} + +func TestPiperPipeData(t *testing.T) { + + c, err := dialPiper(&PiperConfig{ + FindUpstream: func(conn ConnMetadata) (net.Conn, *AuthPipe, error) { + s, err := dialUpstream(simpleEchoHandler, &ServerConfig{NoClientAuth: true}, t) + return s, &AuthPipe{ + UpstreamHostKeyCallback: InsecureIgnoreHostKey(), + }, err + }, + }, nil, t) + if err != nil { t.Fatalf("connect dial to piper: %v", err) }