client_test.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. // Copyright 2014 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package ssh
  5. import (
  6. "bytes"
  7. "crypto/rand"
  8. "errors"
  9. "fmt"
  10. "net"
  11. "strings"
  12. "testing"
  13. )
  14. func TestClientVersion(t *testing.T) {
  15. for _, tt := range []struct {
  16. name string
  17. version string
  18. multiLine string
  19. wantErr bool
  20. }{
  21. {
  22. name: "default version",
  23. version: packageVersion,
  24. },
  25. {
  26. name: "custom version",
  27. version: "SSH-2.0-CustomClientVersionString",
  28. },
  29. {
  30. name: "good multi line version",
  31. version: packageVersion,
  32. multiLine: strings.Repeat("ignored\r\n", 20),
  33. },
  34. {
  35. name: "bad multi line version",
  36. version: packageVersion,
  37. multiLine: "bad multi line version",
  38. wantErr: true,
  39. },
  40. {
  41. name: "long multi line version",
  42. version: packageVersion,
  43. multiLine: strings.Repeat("long multi line version\r\n", 50)[:256],
  44. wantErr: true,
  45. },
  46. } {
  47. t.Run(tt.name, func(t *testing.T) {
  48. c1, c2, err := netPipe()
  49. if err != nil {
  50. t.Fatalf("netPipe: %v", err)
  51. }
  52. defer c1.Close()
  53. defer c2.Close()
  54. go func() {
  55. if tt.multiLine != "" {
  56. c1.Write([]byte(tt.multiLine))
  57. }
  58. NewClientConn(c1, "", &ClientConfig{
  59. ClientVersion: tt.version,
  60. HostKeyCallback: InsecureIgnoreHostKey(),
  61. })
  62. c1.Close()
  63. }()
  64. conf := &ServerConfig{NoClientAuth: true}
  65. conf.AddHostKey(testSigners["rsa"])
  66. conn, _, _, err := NewServerConn(c2, conf)
  67. if err == nil == tt.wantErr {
  68. t.Fatalf("got err %v; wantErr %t", err, tt.wantErr)
  69. }
  70. if tt.wantErr {
  71. // Don't verify the version on an expected error.
  72. return
  73. }
  74. if got := string(conn.ClientVersion()); got != tt.version {
  75. t.Fatalf("got %q; want %q", got, tt.version)
  76. }
  77. })
  78. }
  79. }
  80. func TestHostKeyCheck(t *testing.T) {
  81. for _, tt := range []struct {
  82. name string
  83. wantError string
  84. key PublicKey
  85. }{
  86. {"no callback", "must specify HostKeyCallback", nil},
  87. {"correct key", "", testSigners["rsa"].PublicKey()},
  88. {"mismatch", "mismatch", testSigners["ecdsa"].PublicKey()},
  89. } {
  90. c1, c2, err := netPipe()
  91. if err != nil {
  92. t.Fatalf("netPipe: %v", err)
  93. }
  94. defer c1.Close()
  95. defer c2.Close()
  96. serverConf := &ServerConfig{
  97. NoClientAuth: true,
  98. }
  99. serverConf.AddHostKey(testSigners["rsa"])
  100. go NewServerConn(c1, serverConf)
  101. clientConf := ClientConfig{
  102. User: "user",
  103. }
  104. if tt.key != nil {
  105. clientConf.HostKeyCallback = FixedHostKey(tt.key)
  106. }
  107. _, _, _, err = NewClientConn(c2, "", &clientConf)
  108. if err != nil {
  109. if tt.wantError == "" || !strings.Contains(err.Error(), tt.wantError) {
  110. t.Errorf("%s: got error %q, missing %q", tt.name, err.Error(), tt.wantError)
  111. }
  112. } else if tt.wantError != "" {
  113. t.Errorf("%s: succeeded, but want error string %q", tt.name, tt.wantError)
  114. }
  115. }
  116. }
  117. func TestVerifyHostKeySignature(t *testing.T) {
  118. for _, tt := range []struct {
  119. key string
  120. signAlgo string
  121. verifyAlgo string
  122. wantError string
  123. }{
  124. {"rsa", KeyAlgoRSA, KeyAlgoRSA, ""},
  125. {"rsa", KeyAlgoRSASHA256, KeyAlgoRSASHA256, ""},
  126. {"rsa", KeyAlgoRSA, KeyAlgoRSASHA512, `ssh: invalid signature algorithm "ssh-rsa", expected "rsa-sha2-512"`},
  127. {"ed25519", KeyAlgoED25519, KeyAlgoED25519, ""},
  128. } {
  129. key := testSigners[tt.key].PublicKey()
  130. s, ok := testSigners[tt.key].(AlgorithmSigner)
  131. if !ok {
  132. t.Fatalf("needed an AlgorithmSigner")
  133. }
  134. sig, err := s.SignWithAlgorithm(rand.Reader, []byte("test"), tt.signAlgo)
  135. if err != nil {
  136. t.Fatalf("couldn't sign: %q", err)
  137. }
  138. b := bytes.Buffer{}
  139. writeString(&b, []byte(sig.Format))
  140. writeString(&b, sig.Blob)
  141. result := kexResult{Signature: b.Bytes(), H: []byte("test")}
  142. err = verifyHostKeySignature(key, tt.verifyAlgo, &result)
  143. if err != nil {
  144. if tt.wantError == "" || !strings.Contains(err.Error(), tt.wantError) {
  145. t.Errorf("got error %q, expecting %q", err.Error(), tt.wantError)
  146. }
  147. } else if tt.wantError != "" {
  148. t.Errorf("succeeded, but want error string %q", tt.wantError)
  149. }
  150. }
  151. }
  152. func TestBannerCallback(t *testing.T) {
  153. c1, c2, err := netPipe()
  154. if err != nil {
  155. t.Fatalf("netPipe: %v", err)
  156. }
  157. defer c1.Close()
  158. defer c2.Close()
  159. serverConf := &ServerConfig{
  160. PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
  161. return &Permissions{}, nil
  162. },
  163. BannerCallback: func(conn ConnMetadata) string {
  164. return "Hello World"
  165. },
  166. }
  167. serverConf.AddHostKey(testSigners["rsa"])
  168. go NewServerConn(c1, serverConf)
  169. var receivedBanner string
  170. var bannerCount int
  171. clientConf := ClientConfig{
  172. Auth: []AuthMethod{
  173. Password("123"),
  174. },
  175. User: "user",
  176. HostKeyCallback: InsecureIgnoreHostKey(),
  177. BannerCallback: func(message string) error {
  178. bannerCount++
  179. receivedBanner = message
  180. return nil
  181. },
  182. }
  183. _, _, _, err = NewClientConn(c2, "", &clientConf)
  184. if err != nil {
  185. t.Fatal(err)
  186. }
  187. if bannerCount != 1 {
  188. t.Errorf("got %d banners; want 1", bannerCount)
  189. }
  190. expected := "Hello World"
  191. if receivedBanner != expected {
  192. t.Fatalf("got %s; want %s", receivedBanner, expected)
  193. }
  194. }
  195. func TestNewClientConn(t *testing.T) {
  196. errHostKeyMismatch := errors.New("host key mismatch")
  197. for _, tt := range []struct {
  198. name string
  199. user string
  200. simulateHostKeyMismatch HostKeyCallback
  201. }{
  202. {
  203. name: "good user field for ConnMetadata",
  204. user: "testuser",
  205. },
  206. {
  207. name: "empty user field for ConnMetadata",
  208. user: "",
  209. },
  210. {
  211. name: "host key mismatch",
  212. user: "testuser",
  213. simulateHostKeyMismatch: func(hostname string, remote net.Addr, key PublicKey) error {
  214. return fmt.Errorf("%w: %s", errHostKeyMismatch, bytes.TrimSpace(MarshalAuthorizedKey(key)))
  215. },
  216. },
  217. } {
  218. t.Run(tt.name, func(t *testing.T) {
  219. c1, c2, err := netPipe()
  220. if err != nil {
  221. t.Fatalf("netPipe: %v", err)
  222. }
  223. defer c1.Close()
  224. defer c2.Close()
  225. serverConf := &ServerConfig{
  226. PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
  227. return &Permissions{}, nil
  228. },
  229. }
  230. serverConf.AddHostKey(testSigners["rsa"])
  231. go NewServerConn(c1, serverConf)
  232. clientConf := &ClientConfig{
  233. User: tt.user,
  234. Auth: []AuthMethod{
  235. Password("testpw"),
  236. },
  237. HostKeyCallback: InsecureIgnoreHostKey(),
  238. }
  239. if tt.simulateHostKeyMismatch != nil {
  240. clientConf.HostKeyCallback = tt.simulateHostKeyMismatch
  241. }
  242. clientConn, _, _, err := NewClientConn(c2, "", clientConf)
  243. if err != nil {
  244. if tt.simulateHostKeyMismatch != nil && errors.Is(err, errHostKeyMismatch) {
  245. return
  246. }
  247. t.Fatal(err)
  248. }
  249. if userGot := clientConn.User(); userGot != tt.user {
  250. t.Errorf("got user %q; want user %q", userGot, tt.user)
  251. }
  252. })
  253. }
  254. }
  255. func TestUnsupportedAlgorithm(t *testing.T) {
  256. for _, tt := range []struct {
  257. name string
  258. config Config
  259. wantError string
  260. }{
  261. {
  262. "unsupported KEX",
  263. Config{
  264. KeyExchanges: []string{"unsupported"},
  265. },
  266. "no common algorithm",
  267. },
  268. {
  269. "unsupported and supported KEXs",
  270. Config{
  271. KeyExchanges: []string{"unsupported", kexAlgoCurve25519SHA256},
  272. },
  273. "",
  274. },
  275. {
  276. "unsupported cipher",
  277. Config{
  278. Ciphers: []string{"unsupported"},
  279. },
  280. "no common algorithm",
  281. },
  282. {
  283. "unsupported and supported ciphers",
  284. Config{
  285. Ciphers: []string{"unsupported", chacha20Poly1305ID},
  286. },
  287. "",
  288. },
  289. {
  290. "unsupported MAC",
  291. Config{
  292. MACs: []string{"unsupported"},
  293. // MAC is used for non AAED ciphers.
  294. Ciphers: []string{"aes256-ctr"},
  295. },
  296. "no common algorithm",
  297. },
  298. {
  299. "unsupported and supported MACs",
  300. Config{
  301. MACs: []string{"unsupported", "hmac-sha2-256-etm@openssh.com"},
  302. // MAC is used for non AAED ciphers.
  303. Ciphers: []string{"aes256-ctr"},
  304. },
  305. "",
  306. },
  307. } {
  308. t.Run(tt.name, func(t *testing.T) {
  309. c1, c2, err := netPipe()
  310. if err != nil {
  311. t.Fatalf("netPipe: %v", err)
  312. }
  313. defer c1.Close()
  314. defer c2.Close()
  315. serverConf := &ServerConfig{
  316. Config: tt.config,
  317. PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
  318. return &Permissions{}, nil
  319. },
  320. }
  321. serverConf.AddHostKey(testSigners["rsa"])
  322. go NewServerConn(c1, serverConf)
  323. clientConf := &ClientConfig{
  324. User: "testuser",
  325. Config: tt.config,
  326. Auth: []AuthMethod{
  327. Password("testpw"),
  328. },
  329. HostKeyCallback: InsecureIgnoreHostKey(),
  330. }
  331. _, _, _, err = NewClientConn(c2, "", clientConf)
  332. if err != nil {
  333. if tt.wantError == "" || !strings.Contains(err.Error(), tt.wantError) {
  334. t.Errorf("%s: got error %q, missing %q", tt.name, err.Error(), tt.wantError)
  335. }
  336. } else if tt.wantError != "" {
  337. t.Errorf("%s: succeeded, but want error string %q", tt.name, tt.wantError)
  338. }
  339. })
  340. }
  341. }