1package ssh
2
3import (
4	"bufio"
5	"fmt"
6	"io/ioutil"
7	"os"
8	"strings"
9
10	"golang.org/x/crypto/ssh"
11	"golang.org/x/crypto/ssh/testdata"
12
13	. "gopkg.in/check.v1"
14)
15
16type (
17	SuiteCommon struct{}
18
19	mockKnownHosts struct{}
20)
21
22func (mockKnownHosts) host() string { return "github.com" }
23func (mockKnownHosts) knownHosts() []byte {
24	return []byte(`github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==`)
25}
26func (mockKnownHosts) Network() string { return "tcp" }
27func (mockKnownHosts) String() string  { return "github.com:22" }
28
29var _ = Suite(&SuiteCommon{})
30
31func (s *SuiteCommon) TestKeyboardInteractiveName(c *C) {
32	a := &KeyboardInteractive{
33		User:      "test",
34		Challenge: nil,
35	}
36	c.Assert(a.Name(), Equals, KeyboardInteractiveName)
37}
38
39func (s *SuiteCommon) TestKeyboardInteractiveString(c *C) {
40	a := &KeyboardInteractive{
41		User:      "test",
42		Challenge: nil,
43	}
44	c.Assert(a.String(), Equals, fmt.Sprintf("user: test, name: %s", KeyboardInteractiveName))
45}
46
47func (s *SuiteCommon) TestPasswordName(c *C) {
48	a := &Password{
49		User:     "test",
50		Password: "",
51	}
52	c.Assert(a.Name(), Equals, PasswordName)
53}
54
55func (s *SuiteCommon) TestPasswordString(c *C) {
56	a := &Password{
57		User:     "test",
58		Password: "",
59	}
60	c.Assert(a.String(), Equals, fmt.Sprintf("user: test, name: %s", PasswordName))
61}
62
63func (s *SuiteCommon) TestPasswordCallbackName(c *C) {
64	a := &PasswordCallback{
65		User:     "test",
66		Callback: nil,
67	}
68	c.Assert(a.Name(), Equals, PasswordCallbackName)
69}
70
71func (s *SuiteCommon) TestPasswordCallbackString(c *C) {
72	a := &PasswordCallback{
73		User:     "test",
74		Callback: nil,
75	}
76	c.Assert(a.String(), Equals, fmt.Sprintf("user: test, name: %s", PasswordCallbackName))
77}
78
79func (s *SuiteCommon) TestPublicKeysName(c *C) {
80	a := &PublicKeys{
81		User:   "test",
82		Signer: nil,
83	}
84	c.Assert(a.Name(), Equals, PublicKeysName)
85}
86
87func (s *SuiteCommon) TestPublicKeysString(c *C) {
88	a := &PublicKeys{
89		User:   "test",
90		Signer: nil,
91	}
92	c.Assert(a.String(), Equals, fmt.Sprintf("user: test, name: %s", PublicKeysName))
93}
94
95func (s *SuiteCommon) TestPublicKeysCallbackName(c *C) {
96	a := &PublicKeysCallback{
97		User:     "test",
98		Callback: nil,
99	}
100	c.Assert(a.Name(), Equals, PublicKeysCallbackName)
101}
102
103func (s *SuiteCommon) TestPublicKeysCallbackString(c *C) {
104	a := &PublicKeysCallback{
105		User:     "test",
106		Callback: nil,
107	}
108	c.Assert(a.String(), Equals, fmt.Sprintf("user: test, name: %s", PublicKeysCallbackName))
109}
110func (s *SuiteCommon) TestNewSSHAgentAuth(c *C) {
111	if os.Getenv("SSH_AUTH_SOCK") == "" {
112		c.Skip("SSH_AUTH_SOCK or SSH_TEST_PRIVATE_KEY are required")
113	}
114
115	auth, err := NewSSHAgentAuth("foo")
116	c.Assert(err, IsNil)
117	c.Assert(auth, NotNil)
118}
119
120func (s *SuiteCommon) TestNewSSHAgentAuthNoAgent(c *C) {
121	addr := os.Getenv("SSH_AUTH_SOCK")
122	err := os.Unsetenv("SSH_AUTH_SOCK")
123	c.Assert(err, IsNil)
124
125	defer func() {
126		err := os.Setenv("SSH_AUTH_SOCK", addr)
127		c.Assert(err, IsNil)
128	}()
129
130	k, err := NewSSHAgentAuth("foo")
131	c.Assert(k, IsNil)
132	c.Assert(err, ErrorMatches, ".*SSH_AUTH_SOCK.*|.*SSH agent .* not running.*")
133}
134
135func (*SuiteCommon) TestNewPublicKeys(c *C) {
136	auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"], "")
137	c.Assert(err, IsNil)
138	c.Assert(auth, NotNil)
139}
140
141func (*SuiteCommon) TestNewPublicKeysWithEncryptedPEM(c *C) {
142	f := testdata.PEMEncryptedKeys[0]
143	auth, err := NewPublicKeys("foo", f.PEMBytes, f.EncryptionKey)
144	c.Assert(err, IsNil)
145	c.Assert(auth, NotNil)
146}
147
148func (*SuiteCommon) TestNewPublicKeysFromFile(c *C) {
149	f, err := ioutil.TempFile("", "ssh-test")
150	c.Assert(err, IsNil)
151	_, err = f.Write(testdata.PEMBytes["rsa"])
152	c.Assert(err, IsNil)
153	c.Assert(f.Close(), IsNil)
154	defer os.RemoveAll(f.Name())
155
156	auth, err := NewPublicKeysFromFile("foo", f.Name(), "")
157	c.Assert(err, IsNil)
158	c.Assert(auth, NotNil)
159}
160
161func (*SuiteCommon) TestNewPublicKeysWithInvalidPEM(c *C) {
162	auth, err := NewPublicKeys("foo", []byte("bar"), "")
163	c.Assert(err, NotNil)
164	c.Assert(auth, IsNil)
165}
166
167func (*SuiteCommon) TestNewKnownHostsCallback(c *C) {
168	var mock = mockKnownHosts{}
169
170	f, err := ioutil.TempFile("", "known-hosts")
171	c.Assert(err, IsNil)
172
173	_, err = f.Write(mock.knownHosts())
174	c.Assert(err, IsNil)
175
176	err = f.Close()
177	c.Assert(err, IsNil)
178
179	defer os.RemoveAll(f.Name())
180
181	f, err = os.Open(f.Name())
182	c.Assert(err, IsNil)
183
184	defer f.Close()
185
186	var hostKey ssh.PublicKey
187	scanner := bufio.NewScanner(f)
188	for scanner.Scan() {
189		fields := strings.Split(scanner.Text(), " ")
190		if len(fields) != 3 {
191			continue
192		}
193		if strings.Contains(fields[0], mock.host()) {
194			var err error
195			hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes())
196			if err != nil {
197				c.Fatalf("error parsing %q: %v", fields[2], err)
198			}
199			break
200		}
201	}
202	if hostKey == nil {
203		c.Fatalf("no hostkey for %s", mock.host())
204	}
205
206	clb, err := NewKnownHostsCallback(f.Name())
207	c.Assert(err, IsNil)
208
209	err = clb(mock.String(), mock, hostKey)
210	c.Assert(err, IsNil)
211}
212