client_auth.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. // Copyright 2011 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. "errors"
  8. "fmt"
  9. "io"
  10. )
  11. type authResult int
  12. const (
  13. authFailure authResult = iota
  14. authPartialSuccess
  15. authSuccess
  16. )
  17. // clientAuthenticate authenticates with the remote server. See RFC 4252.
  18. func (c *connection) clientAuthenticate(config *ClientConfig) error {
  19. // initiate user auth session
  20. if err := c.transport.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth})); err != nil {
  21. return err
  22. }
  23. packet, err := c.transport.readPacket()
  24. if err != nil {
  25. return err
  26. }
  27. var serviceAccept serviceAcceptMsg
  28. if err := Unmarshal(packet, &serviceAccept); err != nil {
  29. return err
  30. }
  31. // during the authentication phase the client first attempts the "none" method
  32. // then any untried methods suggested by the server.
  33. var tried []string
  34. var lastMethods []string
  35. sessionID := c.transport.getSessionID()
  36. for auth := AuthMethod(new(noneAuth)); auth != nil; {
  37. ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand)
  38. if err != nil {
  39. return err
  40. }
  41. if ok == authSuccess {
  42. // success
  43. return nil
  44. } else if ok == authFailure {
  45. if m := auth.method(); !contains(tried, m) {
  46. tried = append(tried, m)
  47. }
  48. }
  49. if methods == nil {
  50. methods = lastMethods
  51. }
  52. lastMethods = methods
  53. auth = nil
  54. findNext:
  55. for _, a := range config.Auth {
  56. candidateMethod := a.method()
  57. if contains(tried, candidateMethod) {
  58. continue
  59. }
  60. for _, meth := range methods {
  61. if meth == candidateMethod {
  62. auth = a
  63. break findNext
  64. }
  65. }
  66. }
  67. }
  68. return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", tried)
  69. }
  70. func contains(list []string, e string) bool {
  71. for _, s := range list {
  72. if s == e {
  73. return true
  74. }
  75. }
  76. return false
  77. }
  78. // An AuthMethod represents an instance of an RFC 4252 authentication method.
  79. type AuthMethod interface {
  80. // auth authenticates user over transport t.
  81. // Returns true if authentication is successful.
  82. // If authentication is not successful, a []string of alternative
  83. // method names is returned. If the slice is nil, it will be ignored
  84. // and the previous set of possible methods will be reused.
  85. auth(session []byte, user string, p packetConn, rand io.Reader) (authResult, []string, error)
  86. // method returns the RFC 4252 method name.
  87. method() string
  88. }
  89. // "none" authentication, RFC 4252 section 5.2.
  90. type noneAuth int
  91. func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
  92. if err := c.writePacket(Marshal(&userAuthRequestMsg{
  93. User: user,
  94. Service: serviceSSH,
  95. Method: "none",
  96. })); err != nil {
  97. return authFailure, nil, err
  98. }
  99. return handleAuthResponse(c)
  100. }
  101. func (n *noneAuth) method() string {
  102. return "none"
  103. }
  104. // passwordCallback is an AuthMethod that fetches the password through
  105. // a function call, e.g. by prompting the user.
  106. type passwordCallback func() (password string, err error)
  107. func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
  108. type passwordAuthMsg struct {
  109. User string `sshtype:"50"`
  110. Service string
  111. Method string
  112. Reply bool
  113. Password string
  114. }
  115. pw, err := cb()
  116. // REVIEW NOTE: is there a need to support skipping a password attempt?
  117. // The program may only find out that the user doesn't have a password
  118. // when prompting.
  119. if err != nil {
  120. return authFailure, nil, err
  121. }
  122. if err := c.writePacket(Marshal(&passwordAuthMsg{
  123. User: user,
  124. Service: serviceSSH,
  125. Method: cb.method(),
  126. Reply: false,
  127. Password: pw,
  128. })); err != nil {
  129. return authFailure, nil, err
  130. }
  131. return handleAuthResponse(c)
  132. }
  133. func (cb passwordCallback) method() string {
  134. return "password"
  135. }
  136. // Password returns an AuthMethod using the given password.
  137. func Password(secret string) AuthMethod {
  138. return passwordCallback(func() (string, error) { return secret, nil })
  139. }
  140. // PasswordCallback returns an AuthMethod that uses a callback for
  141. // fetching a password.
  142. func PasswordCallback(prompt func() (secret string, err error)) AuthMethod {
  143. return passwordCallback(prompt)
  144. }
  145. type publickeyAuthMsg struct {
  146. User string `sshtype:"50"`
  147. Service string
  148. Method string
  149. // HasSig indicates to the receiver packet that the auth request is signed and
  150. // should be used for authentication of the request.
  151. HasSig bool
  152. Algoname string
  153. PubKey []byte
  154. // Sig is tagged with "rest" so Marshal will exclude it during
  155. // validateKey
  156. Sig []byte `ssh:"rest"`
  157. }
  158. // publicKeyCallback is an AuthMethod that uses a set of key
  159. // pairs for authentication.
  160. type publicKeyCallback func() ([]Signer, error)
  161. func (cb publicKeyCallback) method() string {
  162. return "publickey"
  163. }
  164. func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
  165. // Authentication is performed by sending an enquiry to test if a key is
  166. // acceptable to the remote. If the key is acceptable, the client will
  167. // attempt to authenticate with the valid key. If not the client will repeat
  168. // the process with the remaining keys.
  169. signers, err := cb()
  170. if err != nil {
  171. return authFailure, nil, err
  172. }
  173. var methods []string
  174. for _, signer := range signers {
  175. ok, err := validateKey(signer.PublicKey(), user, c)
  176. if err != nil {
  177. return authFailure, nil, err
  178. }
  179. if !ok {
  180. continue
  181. }
  182. pub := signer.PublicKey()
  183. pubKey := pub.Marshal()
  184. sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{
  185. User: user,
  186. Service: serviceSSH,
  187. Method: cb.method(),
  188. }, []byte(pub.Type()), pubKey))
  189. if err != nil {
  190. return authFailure, nil, err
  191. }
  192. // manually wrap the serialized signature in a string
  193. s := Marshal(sign)
  194. sig := make([]byte, stringLength(len(s)))
  195. marshalString(sig, s)
  196. msg := publickeyAuthMsg{
  197. User: user,
  198. Service: serviceSSH,
  199. Method: cb.method(),
  200. HasSig: true,
  201. Algoname: pub.Type(),
  202. PubKey: pubKey,
  203. Sig: sig,
  204. }
  205. p := Marshal(&msg)
  206. if err := c.writePacket(p); err != nil {
  207. return authFailure, nil, err
  208. }
  209. var success authResult
  210. success, methods, err = handleAuthResponse(c)
  211. if err != nil {
  212. return authFailure, nil, err
  213. }
  214. // If authentication succeeds or the list of available methods does not
  215. // contain the "publickey" method, do not attempt to authenticate with any
  216. // other keys. According to RFC 4252 Section 7, the latter can occur when
  217. // additional authentication methods are required.
  218. if success == authSuccess || !containsMethod(methods, cb.method()) {
  219. return success, methods, err
  220. }
  221. }
  222. return authFailure, methods, nil
  223. }
  224. func containsMethod(methods []string, method string) bool {
  225. for _, m := range methods {
  226. if m == method {
  227. return true
  228. }
  229. }
  230. return false
  231. }
  232. // validateKey validates the key provided is acceptable to the server.
  233. func validateKey(key PublicKey, user string, c packetConn) (bool, error) {
  234. pubKey := key.Marshal()
  235. msg := publickeyAuthMsg{
  236. User: user,
  237. Service: serviceSSH,
  238. Method: "publickey",
  239. HasSig: false,
  240. Algoname: key.Type(),
  241. PubKey: pubKey,
  242. }
  243. if err := c.writePacket(Marshal(&msg)); err != nil {
  244. return false, err
  245. }
  246. return confirmKeyAck(key, c)
  247. }
  248. func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
  249. pubKey := key.Marshal()
  250. algoname := key.Type()
  251. for {
  252. packet, err := c.readPacket()
  253. if err != nil {
  254. return false, err
  255. }
  256. switch packet[0] {
  257. case msgUserAuthBanner:
  258. if err := handleBannerResponse(c, packet); err != nil {
  259. return false, err
  260. }
  261. case msgUserAuthPubKeyOk:
  262. var msg userAuthPubKeyOkMsg
  263. if err := Unmarshal(packet, &msg); err != nil {
  264. return false, err
  265. }
  266. if msg.Algo != algoname || !bytes.Equal(msg.PubKey, pubKey) {
  267. return false, nil
  268. }
  269. return true, nil
  270. case msgUserAuthFailure:
  271. return false, nil
  272. default:
  273. return false, unexpectedMessageError(msgUserAuthSuccess, packet[0])
  274. }
  275. }
  276. }
  277. // PublicKeys returns an AuthMethod that uses the given key
  278. // pairs.
  279. func PublicKeys(signers ...Signer) AuthMethod {
  280. return publicKeyCallback(func() ([]Signer, error) { return signers, nil })
  281. }
  282. // PublicKeysCallback returns an AuthMethod that runs the given
  283. // function to obtain a list of key pairs.
  284. func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMethod {
  285. return publicKeyCallback(getSigners)
  286. }
  287. // handleAuthResponse returns whether the preceding authentication request succeeded
  288. // along with a list of remaining authentication methods to try next and
  289. // an error if an unexpected response was received.
  290. func handleAuthResponse(c packetConn) (authResult, []string, error) {
  291. for {
  292. packet, err := c.readPacket()
  293. if err != nil {
  294. return authFailure, nil, err
  295. }
  296. switch packet[0] {
  297. case msgUserAuthBanner:
  298. if err := handleBannerResponse(c, packet); err != nil {
  299. return authFailure, nil, err
  300. }
  301. case msgUserAuthFailure:
  302. var msg userAuthFailureMsg
  303. if err := Unmarshal(packet, &msg); err != nil {
  304. return authFailure, nil, err
  305. }
  306. if msg.PartialSuccess {
  307. return authPartialSuccess, msg.Methods, nil
  308. }
  309. return authFailure, msg.Methods, nil
  310. case msgUserAuthSuccess:
  311. return authSuccess, nil, nil
  312. default:
  313. return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
  314. }
  315. }
  316. }
  317. func handleBannerResponse(c packetConn, packet []byte) error {
  318. var msg userAuthBannerMsg
  319. if err := Unmarshal(packet, &msg); err != nil {
  320. return err
  321. }
  322. transport, ok := c.(*handshakeTransport)
  323. if !ok {
  324. return nil
  325. }
  326. if transport.bannerCallback != nil {
  327. return transport.bannerCallback(msg.Message)
  328. }
  329. return nil
  330. }
  331. // KeyboardInteractiveChallenge should print questions, optionally
  332. // disabling echoing (e.g. for passwords), and return all the answers.
  333. // Challenge may be called multiple times in a single session. After
  334. // successful authentication, the server may send a challenge with no
  335. // questions, for which the user and instruction messages should be
  336. // printed. RFC 4256 section 3.3 details how the UI should behave for
  337. // both CLI and GUI environments.
  338. type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error)
  339. // KeyboardInteractive returns an AuthMethod using a prompt/response
  340. // sequence controlled by the server.
  341. func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod {
  342. return challenge
  343. }
  344. func (cb KeyboardInteractiveChallenge) method() string {
  345. return "keyboard-interactive"
  346. }
  347. func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
  348. type initiateMsg struct {
  349. User string `sshtype:"50"`
  350. Service string
  351. Method string
  352. Language string
  353. Submethods string
  354. }
  355. if err := c.writePacket(Marshal(&initiateMsg{
  356. User: user,
  357. Service: serviceSSH,
  358. Method: "keyboard-interactive",
  359. })); err != nil {
  360. return authFailure, nil, err
  361. }
  362. for {
  363. packet, err := c.readPacket()
  364. if err != nil {
  365. return authFailure, nil, err
  366. }
  367. // like handleAuthResponse, but with less options.
  368. switch packet[0] {
  369. case msgUserAuthBanner:
  370. if err := handleBannerResponse(c, packet); err != nil {
  371. return authFailure, nil, err
  372. }
  373. continue
  374. case msgUserAuthInfoRequest:
  375. // OK
  376. case msgUserAuthFailure:
  377. var msg userAuthFailureMsg
  378. if err := Unmarshal(packet, &msg); err != nil {
  379. return authFailure, nil, err
  380. }
  381. if msg.PartialSuccess {
  382. return authPartialSuccess, msg.Methods, nil
  383. }
  384. return authFailure, msg.Methods, nil
  385. case msgUserAuthSuccess:
  386. return authSuccess, nil, nil
  387. default:
  388. return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
  389. }
  390. var msg userAuthInfoRequestMsg
  391. if err := Unmarshal(packet, &msg); err != nil {
  392. return authFailure, nil, err
  393. }
  394. // Manually unpack the prompt/echo pairs.
  395. rest := msg.Prompts
  396. var prompts []string
  397. var echos []bool
  398. for i := 0; i < int(msg.NumPrompts); i++ {
  399. prompt, r, ok := parseString(rest)
  400. if !ok || len(r) == 0 {
  401. return authFailure, nil, errors.New("ssh: prompt format error")
  402. }
  403. prompts = append(prompts, string(prompt))
  404. echos = append(echos, r[0] != 0)
  405. rest = r[1:]
  406. }
  407. if len(rest) != 0 {
  408. return authFailure, nil, errors.New("ssh: extra data following keyboard-interactive pairs")
  409. }
  410. answers, err := cb(msg.User, msg.Instruction, prompts, echos)
  411. if err != nil {
  412. return authFailure, nil, err
  413. }
  414. if len(answers) != len(prompts) {
  415. return authFailure, nil, fmt.Errorf("ssh: incorrect number of answers from keyboard-interactive callback %d (expected %d)", len(answers), len(prompts))
  416. }
  417. responseLength := 1 + 4
  418. for _, a := range answers {
  419. responseLength += stringLength(len(a))
  420. }
  421. serialized := make([]byte, responseLength)
  422. p := serialized
  423. p[0] = msgUserAuthInfoResponse
  424. p = p[1:]
  425. p = marshalUint32(p, uint32(len(answers)))
  426. for _, a := range answers {
  427. p = marshalString(p, []byte(a))
  428. }
  429. if err := c.writePacket(serialized); err != nil {
  430. return authFailure, nil, err
  431. }
  432. }
  433. }
  434. type retryableAuthMethod struct {
  435. authMethod AuthMethod
  436. maxTries int
  437. }
  438. func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok authResult, methods []string, err error) {
  439. for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ {
  440. ok, methods, err = r.authMethod.auth(session, user, c, rand)
  441. if ok != authFailure || err != nil { // either success, partial success or error terminate
  442. return ok, methods, err
  443. }
  444. }
  445. return ok, methods, err
  446. }
  447. func (r *retryableAuthMethod) method() string {
  448. return r.authMethod.method()
  449. }
  450. // RetryableAuthMethod is a decorator for other auth methods enabling them to
  451. // be retried up to maxTries before considering that AuthMethod itself failed.
  452. // If maxTries is <= 0, will retry indefinitely
  453. //
  454. // This is useful for interactive clients using challenge/response type
  455. // authentication (e.g. Keyboard-Interactive, Password, etc) where the user
  456. // could mistype their response resulting in the server issuing a
  457. // SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4
  458. // [keyboard-interactive]); Without this decorator, the non-retryable
  459. // AuthMethod would be removed from future consideration, and never tried again
  460. // (and so the user would never be able to retry their entry).
  461. func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod {
  462. return &retryableAuthMethod{authMethod: auth, maxTries: maxTries}
  463. }
  464. // GSSAPIWithMICAuthMethod is an AuthMethod with "gssapi-with-mic" authentication.
  465. // See RFC 4462 section 3
  466. // gssAPIClient is implementation of the GSSAPIClient interface, see the definition of the interface for details.
  467. // target is the server host you want to log in to.
  468. func GSSAPIWithMICAuthMethod(gssAPIClient GSSAPIClient, target string) AuthMethod {
  469. if gssAPIClient == nil {
  470. panic("gss-api client must be not nil with enable gssapi-with-mic")
  471. }
  472. return &gssAPIWithMICCallback{gssAPIClient: gssAPIClient, target: target}
  473. }
  474. type gssAPIWithMICCallback struct {
  475. gssAPIClient GSSAPIClient
  476. target string
  477. }
  478. func (g *gssAPIWithMICCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
  479. m := &userAuthRequestMsg{
  480. User: user,
  481. Service: serviceSSH,
  482. Method: g.method(),
  483. }
  484. // The GSS-API authentication method is initiated when the client sends an SSH_MSG_USERAUTH_REQUEST.
  485. // See RFC 4462 section 3.2.
  486. m.Payload = appendU32(m.Payload, 1)
  487. m.Payload = appendString(m.Payload, string(krb5OID))
  488. if err := c.writePacket(Marshal(m)); err != nil {
  489. return authFailure, nil, err
  490. }
  491. // The server responds to the SSH_MSG_USERAUTH_REQUEST with either an
  492. // SSH_MSG_USERAUTH_FAILURE if none of the mechanisms are supported or
  493. // with an SSH_MSG_USERAUTH_GSSAPI_RESPONSE.
  494. // See RFC 4462 section 3.3.
  495. // OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,so I don't want to check
  496. // selected mech if it is valid.
  497. packet, err := c.readPacket()
  498. if err != nil {
  499. return authFailure, nil, err
  500. }
  501. userAuthGSSAPIResp := &userAuthGSSAPIResponse{}
  502. if err := Unmarshal(packet, userAuthGSSAPIResp); err != nil {
  503. return authFailure, nil, err
  504. }
  505. // Start the loop into the exchange token.
  506. // See RFC 4462 section 3.4.
  507. var token []byte
  508. defer g.gssAPIClient.DeleteSecContext()
  509. for {
  510. // Initiates the establishment of a security context between the application and a remote peer.
  511. nextToken, needContinue, err := g.gssAPIClient.InitSecContext("host@"+g.target, token, false)
  512. if err != nil {
  513. return authFailure, nil, err
  514. }
  515. if len(nextToken) > 0 {
  516. if err := c.writePacket(Marshal(&userAuthGSSAPIToken{
  517. Token: nextToken,
  518. })); err != nil {
  519. return authFailure, nil, err
  520. }
  521. }
  522. if !needContinue {
  523. break
  524. }
  525. packet, err = c.readPacket()
  526. if err != nil {
  527. return authFailure, nil, err
  528. }
  529. switch packet[0] {
  530. case msgUserAuthFailure:
  531. var msg userAuthFailureMsg
  532. if err := Unmarshal(packet, &msg); err != nil {
  533. return authFailure, nil, err
  534. }
  535. if msg.PartialSuccess {
  536. return authPartialSuccess, msg.Methods, nil
  537. }
  538. return authFailure, msg.Methods, nil
  539. case msgUserAuthGSSAPIError:
  540. userAuthGSSAPIErrorResp := &userAuthGSSAPIError{}
  541. if err := Unmarshal(packet, userAuthGSSAPIErrorResp); err != nil {
  542. return authFailure, nil, err
  543. }
  544. return authFailure, nil, fmt.Errorf("GSS-API Error:\n"+
  545. "Major Status: %d\n"+
  546. "Minor Status: %d\n"+
  547. "Error Message: %s\n", userAuthGSSAPIErrorResp.MajorStatus, userAuthGSSAPIErrorResp.MinorStatus,
  548. userAuthGSSAPIErrorResp.Message)
  549. case msgUserAuthGSSAPIToken:
  550. userAuthGSSAPITokenReq := &userAuthGSSAPIToken{}
  551. if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
  552. return authFailure, nil, err
  553. }
  554. token = userAuthGSSAPITokenReq.Token
  555. }
  556. }
  557. // Binding Encryption Keys.
  558. // See RFC 4462 section 3.5.
  559. micField := buildMIC(string(session), user, "ssh-connection", "gssapi-with-mic")
  560. micToken, err := g.gssAPIClient.GetMIC(micField)
  561. if err != nil {
  562. return authFailure, nil, err
  563. }
  564. if err := c.writePacket(Marshal(&userAuthGSSAPIMIC{
  565. MIC: micToken,
  566. })); err != nil {
  567. return authFailure, nil, err
  568. }
  569. return handleAuthResponse(c)
  570. }
  571. func (g *gssAPIWithMICCallback) method() string {
  572. return "gssapi-with-mic"
  573. }