diff options
Diffstat (limited to 'plumbing/transport')
-rw-r--r-- | plumbing/transport/ssh/auth_method.go | 33 | ||||
-rw-r--r-- | plumbing/transport/ssh/auth_method_test.go | 11 | ||||
-rw-r--r-- | plumbing/transport/ssh/common.go | 8 | ||||
-rw-r--r-- | plumbing/transport/ssh/upload_pack_test.go | 19 |
4 files changed, 57 insertions, 14 deletions
diff --git a/plumbing/transport/ssh/auth_method.go b/plumbing/transport/ssh/auth_method.go index ad92ee1..a3e1ad1 100644 --- a/plumbing/transport/ssh/auth_method.go +++ b/plumbing/transport/ssh/auth_method.go @@ -1,6 +1,8 @@ package ssh import ( + "crypto/x509" + "encoding/pem" "errors" "fmt" "io/ioutil" @@ -9,9 +11,11 @@ import ( "os/user" "path/filepath" - "github.com/src-d/crypto/ssh/knownhosts" + "gopkg.in/src-d/go-git.v4/plumbing/transport" + "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" + "golang.org/x/crypto/ssh/knownhosts" ) const DefaultUsername = "git" @@ -22,6 +26,7 @@ var ErrEmptySSHAgentAddr = errors.New("SSH_AUTH_SOCK env variable is required") // must implement. The clientConfig method returns the ssh client // configuration needed to establish an ssh connection. type AuthMethod interface { + transport.AuthMethod clientConfig() *ssh.ClientConfig hostKeyCallback() (ssh.HostKeyCallback, error) } @@ -112,9 +117,22 @@ type PublicKeys struct { baseAuthMethod } -// NewPublicKeys returns a PublicKeys from a PEM encoded private key. It -// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys. -func NewPublicKeys(user string, pemBytes []byte) (AuthMethod, error) { +// NewPublicKeys returns a PublicKeys from a PEM encoded private key. An +// encryption password should be given if the pemBytes contains a password +// encrypted PEM block otherwise password should be empty. It supports RSA +// (PKCS#1), DSA (OpenSSL), and ECDSA private keys. +func NewPublicKeys(user string, pemBytes []byte, password string) (AuthMethod, error) { + block, _ := pem.Decode(pemBytes) + if x509.IsEncryptedPEMBlock(block) { + key, err := x509.DecryptPEMBlock(block, []byte(password)) + if err != nil { + return nil, err + } + + block = &pem.Block{Type: block.Type, Bytes: key} + pemBytes = pem.EncodeToMemory(block) + } + signer, err := ssh.ParsePrivateKey(pemBytes) if err != nil { return nil, err @@ -124,14 +142,15 @@ func NewPublicKeys(user string, pemBytes []byte) (AuthMethod, error) { } // NewPublicKeysFromFile returns a PublicKeys from a file containing a PEM -// encoded private key. -func NewPublicKeysFromFile(user string, pemFile string) (AuthMethod, error) { +// encoded private key. An encryption password should be given if the pemBytes +// contains a password encrypted PEM block otherwise password should be empty. +func NewPublicKeysFromFile(user, pemFile, password string) (AuthMethod, error) { bytes, err := ioutil.ReadFile(pemFile) if err != nil { return nil, err } - return NewPublicKeys(user, bytes) + return NewPublicKeys(user, bytes, password) } func (a *PublicKeys) Name() string { diff --git a/plumbing/transport/ssh/auth_method_test.go b/plumbing/transport/ssh/auth_method_test.go index 43b34df..6f3d82f 100644 --- a/plumbing/transport/ssh/auth_method_test.go +++ b/plumbing/transport/ssh/auth_method_test.go @@ -109,7 +109,14 @@ func (s *SuiteCommon) TestNewSSHAgentAuth(c *C) { } func (*SuiteCommon) TestNewPublicKeys(c *C) { - auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"]) + auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"], "") + c.Assert(err, IsNil) + c.Assert(auth, NotNil) +} + +func (*SuiteCommon) TestNewPublicKeysWithEncryptedPEM(c *C) { + f := testdata.PEMEncryptedKeys[0] + auth, err := NewPublicKeys("foo", f.PEMBytes, f.EncryptionKey) c.Assert(err, IsNil) c.Assert(auth, NotNil) } @@ -122,7 +129,7 @@ func (*SuiteCommon) TestNewPublicKeysFromFile(c *C) { c.Assert(f.Close(), IsNil) defer os.RemoveAll(f.Name()) - auth, err := NewPublicKeysFromFile("foo", f.Name()) + auth, err := NewPublicKeysFromFile("foo", f.Name(), "") c.Assert(err, IsNil) c.Assert(auth, NotNil) } diff --git a/plumbing/transport/ssh/common.go b/plumbing/transport/ssh/common.go index 9b484f9..7b44a91 100644 --- a/plumbing/transport/ssh/common.go +++ b/plumbing/transport/ssh/common.go @@ -14,6 +14,12 @@ import ( // DefaultClient is the default SSH client. var DefaultClient = common.NewClient(&runner{}) +// DefaultAuthBuilder is the function used to create a default AuthMethod, when +// the user doesn't provide any. +var DefaultAuthBuilder = func(user string) (AuthMethod, error) { + return NewSSHAgentAuth(user) +} + type runner struct{} func (r *runner) Command(cmd string, ep transport.Endpoint, auth transport.AuthMethod) (common.Command, error) { @@ -119,7 +125,7 @@ func (c *command) setAuthFromEndpoint() error { } var err error - c.auth, err = NewSSHAgentAuth(u) + c.auth, err = DefaultAuthBuilder(u) return err } diff --git a/plumbing/transport/ssh/upload_pack_test.go b/plumbing/transport/ssh/upload_pack_test.go index 54d523a..cb9baa5 100644 --- a/plumbing/transport/ssh/upload_pack_test.go +++ b/plumbing/transport/ssh/upload_pack_test.go @@ -16,10 +16,7 @@ type UploadPackSuite struct { var _ = Suite(&UploadPackSuite{}) func (s *UploadPackSuite) SetUpSuite(c *C) { - if os.Getenv("SSH_AUTH_SOCK") == "" { - c.Skip("SSH_AUTH_SOCK is not set") - } - + s.setAuthBuilder(c) s.UploadPackSuite.Client = DefaultClient ep, err := transport.NewEndpoint("git@github.com:git-fixtures/basic.git") @@ -34,3 +31,17 @@ func (s *UploadPackSuite) SetUpSuite(c *C) { c.Assert(err, IsNil) s.UploadPackSuite.NonExistentEndpoint = ep } + +func (s *UploadPackSuite) setAuthBuilder(c *C) { + privateKey := os.Getenv("SSH_TEST_PRIVATE_KEY") + if privateKey != "" { + DefaultAuthBuilder = func(user string) (AuthMethod, error) { + return NewPublicKeysFromFile(user, privateKey, "") + } + } + + if privateKey == "" && os.Getenv("SSH_AUTH_SOCK") == "" { + c.Skip("SSH_AUTH_SOCK or SSH_TEST_PRIVATE_KEY are required") + return + } +} |