client_auth.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  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. "strings"
  11. )
  12. type authResult int
  13. const (
  14. authFailure authResult = iota
  15. authPartialSuccess
  16. authSuccess
  17. )
  18. // clientAuthenticate authenticates with the remote server. See RFC 4252.
  19. func (c *connection) clientAuthenticate(config *ClientConfig) error {
  20. // initiate user auth session
  21. if err := c.transport.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth})); err != nil {
  22. return err
  23. }
  24. packet, err := c.transport.readPacket()
  25. if err != nil {
  26. return err
  27. }
  28. // The server may choose to send a SSH_MSG_EXT_INFO at this point (if we
  29. // advertised willingness to receive one, which we always do) or not. See
  30. // RFC 8308, Section 2.4.
  31. extensions := make(map[string][]byte)
  32. if len(packet) > 0 && packet[0] == msgExtInfo {
  33. var extInfo extInfoMsg
  34. if err := Unmarshal(packet, &extInfo); err != nil {
  35. return err
  36. }
  37. payload := extInfo.Payload
  38. for i := uint32(0); i < extInfo.NumExtensions; i++ {
  39. name, rest, ok := parseString(payload)
  40. if !ok {
  41. return parseError(msgExtInfo)
  42. }
  43. value, rest, ok := parseString(rest)
  44. if !ok {
  45. return parseError(msgExtInfo)
  46. }
  47. extensions[string(name)] = value
  48. payload = rest
  49. }
  50. packet, err = c.transport.readPacket()
  51. if err != nil {
  52. return err
  53. }
  54. }
  55. var serviceAccept serviceAcceptMsg
  56. if err := Unmarshal(packet, &serviceAccept); err != nil {
  57. return err
  58. }
  59. // during the authentication phase the client first attempts the "none" method
  60. // then any untried methods suggested by the server.
  61. var tried []string
  62. var lastMethods []string
  63. sessionID := c.transport.getSessionID()
  64. for auth := AuthMethod(new(noneAuth)); auth != nil; {
  65. ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions)
  66. if err != nil {
  67. // On disconnect, return error immediately
  68. if _, ok := err.(*disconnectMsg); ok {
  69. return err
  70. }
  71. // We return the error later if there is no other method left to
  72. // try.
  73. ok = authFailure
  74. }
  75. if ok == authSuccess {
  76. // success
  77. return nil
  78. } else if ok == authFailure {
  79. if m := auth.method(); !contains(tried, m) {
  80. tried = append(tried, m)
  81. }
  82. }
  83. if methods == nil {
  84. methods = lastMethods
  85. }
  86. lastMethods = methods
  87. auth = nil
  88. findNext:
  89. for _, a := range config.Auth {
  90. candidateMethod := a.method()
  91. if contains(tried, candidateMethod) {
  92. continue
  93. }
  94. for _, meth := range methods {
  95. if meth == candidateMethod {
  96. auth = a
  97. break findNext
  98. }
  99. }
  100. }
  101. if auth == nil && err != nil {
  102. // We have an error and there are no other authentication methods to
  103. // try, so we return it.
  104. return err
  105. }
  106. }
  107. return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", tried)
  108. }
  109. func contains(list []string, e string) bool {
  110. for _, s := range list {
  111. if s == e {
  112. return true
  113. }
  114. }
  115. return false
  116. }
  117. // An AuthMethod represents an instance of an RFC 4252 authentication method.
  118. type AuthMethod interface {
  119. // auth authenticates user over transport t.
  120. // Returns true if authentication is successful.
  121. // If authentication is not successful, a []string of alternative
  122. // method names is returned. If the slice is nil, it will be ignored
  123. // and the previous set of possible methods will be reused.
  124. auth(session []byte, user string, p packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error)
  125. // method returns the RFC 4252 method name.
  126. method() string
  127. }
  128. // "none" authentication, RFC 4252 section 5.2.
  129. type noneAuth int
  130. func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) {
  131. if err := c.writePacket(Marshal(&userAuthRequestMsg{
  132. User: user,
  133. Service: serviceSSH,
  134. Method: "none",
  135. })); err != nil {
  136. return authFailure, nil, err
  137. }
  138. return handleAuthResponse(c)
  139. }
  140. func (n *noneAuth) method() string {
  141. return "none"
  142. }
  143. // passwordCallback is an AuthMethod that fetches the password through
  144. // a function call, e.g. by prompting the user.
  145. type passwordCallback func() (password string, err error)
  146. func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) {
  147. type passwordAuthMsg struct {
  148. User string `sshtype:"50"`
  149. Service string
  150. Method string
  151. Reply bool
  152. Password string
  153. }
  154. pw, err := cb()
  155. // REVIEW NOTE: is there a need to support skipping a password attempt?
  156. // The program may only find out that the user doesn't have a password
  157. // when prompting.
  158. if err != nil {
  159. return authFailure, nil, err
  160. }
  161. if err := c.writePacket(Marshal(&passwordAuthMsg{
  162. User: user,
  163. Service: serviceSSH,
  164. Method: cb.method(),
  165. Reply: false,
  166. Password: pw,
  167. })); err != nil {
  168. return authFailure, nil, err
  169. }
  170. return handleAuthResponse(c)
  171. }
  172. func (cb passwordCallback) method() string {
  173. return "password"
  174. }
  175. // Password returns an AuthMethod using the given password.
  176. func Password(secret string) AuthMethod {
  177. return passwordCallback(func() (string, error) { return secret, nil })
  178. }
  179. // PasswordCallback returns an AuthMethod that uses a callback for
  180. // fetching a password.
  181. func PasswordCallback(prompt func() (secret string, err error)) AuthMethod {
  182. return passwordCallback(prompt)
  183. }
  184. type publickeyAuthMsg struct {
  185. User string `sshtype:"50"`
  186. Service string
  187. Method string
  188. // HasSig indicates to the receiver packet that the auth request is signed and
  189. // should be used for authentication of the request.
  190. HasSig bool
  191. Algoname string
  192. PubKey []byte
  193. // Sig is tagged with "rest" so Marshal will exclude it during
  194. // validateKey
  195. Sig []byte `ssh:"rest"`
  196. }
  197. // publicKeyCallback is an AuthMethod that uses a set of key
  198. // pairs for authentication.
  199. type publicKeyCallback func() ([]Signer, error)
  200. func (cb publicKeyCallback) method() string {
  201. return "publickey"
  202. }
  203. func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (MultiAlgorithmSigner, string, error) {
  204. var as MultiAlgorithmSigner
  205. keyFormat := signer.PublicKey().Type()
  206. // If the signer implements MultiAlgorithmSigner we use the algorithms it
  207. // support, if it implements AlgorithmSigner we assume it supports all
  208. // algorithms, otherwise only the key format one.
  209. switch s := signer.(type) {
  210. case MultiAlgorithmSigner:
  211. as = s
  212. case AlgorithmSigner:
  213. as = &multiAlgorithmSigner{
  214. AlgorithmSigner: s,
  215. supportedAlgorithms: algorithmsForKeyFormat(underlyingAlgo(keyFormat)),
  216. }
  217. default:
  218. as = &multiAlgorithmSigner{
  219. AlgorithmSigner: algorithmSignerWrapper{signer},
  220. supportedAlgorithms: []string{underlyingAlgo(keyFormat)},
  221. }
  222. }
  223. getFallbackAlgo := func() (string, error) {
  224. // Fallback to use if there is no "server-sig-algs" extension or a
  225. // common algorithm cannot be found. We use the public key format if the
  226. // MultiAlgorithmSigner supports it, otherwise we return an error.
  227. if !contains(as.Algorithms(), underlyingAlgo(keyFormat)) {
  228. return "", fmt.Errorf("ssh: no common public key signature algorithm, server only supports %q for key type %q, signer only supports %v",
  229. underlyingAlgo(keyFormat), keyFormat, as.Algorithms())
  230. }
  231. return keyFormat, nil
  232. }
  233. extPayload, ok := extensions["server-sig-algs"]
  234. if !ok {
  235. // If there is no "server-sig-algs" extension use the fallback
  236. // algorithm.
  237. algo, err := getFallbackAlgo()
  238. return as, algo, err
  239. }
  240. // The server-sig-algs extension only carries underlying signature
  241. // algorithm, but we are trying to select a protocol-level public key
  242. // algorithm, which might be a certificate type. Extend the list of server
  243. // supported algorithms to include the corresponding certificate algorithms.
  244. serverAlgos := strings.Split(string(extPayload), ",")
  245. for _, algo := range serverAlgos {
  246. if certAlgo, ok := certificateAlgo(algo); ok {
  247. serverAlgos = append(serverAlgos, certAlgo)
  248. }
  249. }
  250. // Filter algorithms based on those supported by MultiAlgorithmSigner.
  251. var keyAlgos []string
  252. for _, algo := range algorithmsForKeyFormat(keyFormat) {
  253. if contains(as.Algorithms(), underlyingAlgo(algo)) {
  254. keyAlgos = append(keyAlgos, algo)
  255. }
  256. }
  257. algo, err := findCommon("public key signature algorithm", keyAlgos, serverAlgos)
  258. if err != nil {
  259. // If there is no overlap, return the fallback algorithm to support
  260. // servers that fail to list all supported algorithms.
  261. algo, err := getFallbackAlgo()
  262. return as, algo, err
  263. }
  264. return as, algo, nil
  265. }
  266. func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error) {
  267. // Authentication is performed by sending an enquiry to test if a key is
  268. // acceptable to the remote. If the key is acceptable, the client will
  269. // attempt to authenticate with the valid key. If not the client will repeat
  270. // the process with the remaining keys.
  271. signers, err := cb()
  272. if err != nil {
  273. return authFailure, nil, err
  274. }
  275. var methods []string
  276. var errSigAlgo error
  277. origSignersLen := len(signers)
  278. for idx := 0; idx < len(signers); idx++ {
  279. signer := signers[idx]
  280. pub := signer.PublicKey()
  281. as, algo, err := pickSignatureAlgorithm(signer, extensions)
  282. if err != nil && errSigAlgo == nil {
  283. // If we cannot negotiate a signature algorithm store the first
  284. // error so we can return it to provide a more meaningful message if
  285. // no other signers work.
  286. errSigAlgo = err
  287. continue
  288. }
  289. ok, err := validateKey(pub, algo, user, c)
  290. if err != nil {
  291. return authFailure, nil, err
  292. }
  293. // OpenSSH 7.2-7.7 advertises support for rsa-sha2-256 and rsa-sha2-512
  294. // in the "server-sig-algs" extension but doesn't support these
  295. // algorithms for certificate authentication, so if the server rejects
  296. // the key try to use the obtained algorithm as if "server-sig-algs" had
  297. // not been implemented if supported from the algorithm signer.
  298. if !ok && idx < origSignersLen && isRSACert(algo) && algo != CertAlgoRSAv01 {
  299. if contains(as.Algorithms(), KeyAlgoRSA) {
  300. // We retry using the compat algorithm after all signers have
  301. // been tried normally.
  302. signers = append(signers, &multiAlgorithmSigner{
  303. AlgorithmSigner: as,
  304. supportedAlgorithms: []string{KeyAlgoRSA},
  305. })
  306. }
  307. }
  308. if !ok {
  309. continue
  310. }
  311. pubKey := pub.Marshal()
  312. data := buildDataSignedForAuth(session, userAuthRequestMsg{
  313. User: user,
  314. Service: serviceSSH,
  315. Method: cb.method(),
  316. }, algo, pubKey)
  317. sign, err := as.SignWithAlgorithm(rand, data, underlyingAlgo(algo))
  318. if err != nil {
  319. return authFailure, nil, err
  320. }
  321. // manually wrap the serialized signature in a string
  322. s := Marshal(sign)
  323. sig := make([]byte, stringLength(len(s)))
  324. marshalString(sig, s)
  325. msg := publickeyAuthMsg{
  326. User: user,
  327. Service: serviceSSH,
  328. Method: cb.method(),
  329. HasSig: true,
  330. Algoname: algo,
  331. PubKey: pubKey,
  332. Sig: sig,
  333. }
  334. p := Marshal(&msg)
  335. if err := c.writePacket(p); err != nil {
  336. return authFailure, nil, err
  337. }
  338. var success authResult
  339. success, methods, err = handleAuthResponse(c)
  340. if err != nil {
  341. return authFailure, nil, err
  342. }
  343. // If authentication succeeds or the list of available methods does not
  344. // contain the "publickey" method, do not attempt to authenticate with any
  345. // other keys. According to RFC 4252 Section 7, the latter can occur when
  346. // additional authentication methods are required.
  347. if success == authSuccess || !contains(methods, cb.method()) {
  348. return success, methods, err
  349. }
  350. }
  351. return authFailure, methods, errSigAlgo
  352. }
  353. // validateKey validates the key provided is acceptable to the server.
  354. func validateKey(key PublicKey, algo string, user string, c packetConn) (bool, error) {
  355. pubKey := key.Marshal()
  356. msg := publickeyAuthMsg{
  357. User: user,
  358. Service: serviceSSH,
  359. Method: "publickey",
  360. HasSig: false,
  361. Algoname: algo,
  362. PubKey: pubKey,
  363. }
  364. if err := c.writePacket(Marshal(&msg)); err != nil {
  365. return false, err
  366. }
  367. return confirmKeyAck(key, c)
  368. }
  369. func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
  370. pubKey := key.Marshal()
  371. for {
  372. packet, err := c.readPacket()
  373. if err != nil {
  374. return false, err
  375. }
  376. switch packet[0] {
  377. case msgUserAuthBanner:
  378. if err := handleBannerResponse(c, packet); err != nil {
  379. return false, err
  380. }
  381. case msgUserAuthPubKeyOk:
  382. var msg userAuthPubKeyOkMsg
  383. if err := Unmarshal(packet, &msg); err != nil {
  384. return false, err
  385. }
  386. // According to RFC 4252 Section 7 the algorithm in
  387. // SSH_MSG_USERAUTH_PK_OK should match that of the request but some
  388. // servers send the key type instead. OpenSSH allows any algorithm
  389. // that matches the public key, so we do the same.
  390. // https://github.com/openssh/openssh-portable/blob/86bdd385/sshconnect2.c#L709
  391. if !contains(algorithmsForKeyFormat(key.Type()), msg.Algo) {
  392. return false, nil
  393. }
  394. if !bytes.Equal(msg.PubKey, pubKey) {
  395. return false, nil
  396. }
  397. return true, nil
  398. case msgUserAuthFailure:
  399. return false, nil
  400. default:
  401. return false, unexpectedMessageError(msgUserAuthPubKeyOk, packet[0])
  402. }
  403. }
  404. }
  405. // PublicKeys returns an AuthMethod that uses the given key
  406. // pairs.
  407. func PublicKeys(signers ...Signer) AuthMethod {
  408. return publicKeyCallback(func() ([]Signer, error) { return signers, nil })
  409. }
  410. // PublicKeysCallback returns an AuthMethod that runs the given
  411. // function to obtain a list of key pairs.
  412. func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMethod {
  413. return publicKeyCallback(getSigners)
  414. }
  415. // handleAuthResponse returns whether the preceding authentication request succeeded
  416. // along with a list of remaining authentication methods to try next and
  417. // an error if an unexpected response was received.
  418. func handleAuthResponse(c packetConn) (authResult, []string, error) {
  419. gotMsgExtInfo := false
  420. for {
  421. packet, err := c.readPacket()
  422. if err != nil {
  423. return authFailure, nil, err
  424. }
  425. switch packet[0] {
  426. case msgUserAuthBanner:
  427. if err := handleBannerResponse(c, packet); err != nil {
  428. return authFailure, nil, err
  429. }
  430. case msgExtInfo:
  431. // Ignore post-authentication RFC 8308 extensions, once.
  432. if gotMsgExtInfo {
  433. return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
  434. }
  435. gotMsgExtInfo = true
  436. case msgUserAuthFailure:
  437. var msg userAuthFailureMsg
  438. if err := Unmarshal(packet, &msg); err != nil {
  439. return authFailure, nil, err
  440. }
  441. if msg.PartialSuccess {
  442. return authPartialSuccess, msg.Methods, nil
  443. }
  444. return authFailure, msg.Methods, nil
  445. case msgUserAuthSuccess:
  446. return authSuccess, nil, nil
  447. default:
  448. return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
  449. }
  450. }
  451. }
  452. func handleBannerResponse(c packetConn, packet []byte) error {
  453. var msg userAuthBannerMsg
  454. if err := Unmarshal(packet, &msg); err != nil {
  455. return err
  456. }
  457. transport, ok := c.(*handshakeTransport)
  458. if !ok {
  459. return nil
  460. }
  461. if transport.bannerCallback != nil {
  462. return transport.bannerCallback(msg.Message)
  463. }
  464. return nil
  465. }
  466. // KeyboardInteractiveChallenge should print questions, optionally
  467. // disabling echoing (e.g. for passwords), and return all the answers.
  468. // Challenge may be called multiple times in a single session. After
  469. // successful authentication, the server may send a challenge with no
  470. // questions, for which the name and instruction messages should be
  471. // printed. RFC 4256 section 3.3 details how the UI should behave for
  472. // both CLI and GUI environments.
  473. type KeyboardInteractiveChallenge func(name, instruction string, questions []string, echos []bool) (answers []string, err error)
  474. // KeyboardInteractive returns an AuthMethod using a prompt/response
  475. // sequence controlled by the server.
  476. func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod {
  477. return challenge
  478. }
  479. func (cb KeyboardInteractiveChallenge) method() string {
  480. return "keyboard-interactive"
  481. }
  482. func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) {
  483. type initiateMsg struct {
  484. User string `sshtype:"50"`
  485. Service string
  486. Method string
  487. Language string
  488. Submethods string
  489. }
  490. if err := c.writePacket(Marshal(&initiateMsg{
  491. User: user,
  492. Service: serviceSSH,
  493. Method: "keyboard-interactive",
  494. })); err != nil {
  495. return authFailure, nil, err
  496. }
  497. gotMsgExtInfo := false
  498. gotUserAuthInfoRequest := false
  499. for {
  500. packet, err := c.readPacket()
  501. if err != nil {
  502. return authFailure, nil, err
  503. }
  504. // like handleAuthResponse, but with less options.
  505. switch packet[0] {
  506. case msgUserAuthBanner:
  507. if err := handleBannerResponse(c, packet); err != nil {
  508. return authFailure, nil, err
  509. }
  510. continue
  511. case msgExtInfo:
  512. // Ignore post-authentication RFC 8308 extensions, once.
  513. if gotMsgExtInfo {
  514. return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
  515. }
  516. gotMsgExtInfo = true
  517. continue
  518. case msgUserAuthInfoRequest:
  519. // OK
  520. case msgUserAuthFailure:
  521. var msg userAuthFailureMsg
  522. if err := Unmarshal(packet, &msg); err != nil {
  523. return authFailure, nil, err
  524. }
  525. if msg.PartialSuccess {
  526. return authPartialSuccess, msg.Methods, nil
  527. }
  528. if !gotUserAuthInfoRequest {
  529. return authFailure, msg.Methods, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
  530. }
  531. return authFailure, msg.Methods, nil
  532. case msgUserAuthSuccess:
  533. return authSuccess, nil, nil
  534. default:
  535. return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
  536. }
  537. var msg userAuthInfoRequestMsg
  538. if err := Unmarshal(packet, &msg); err != nil {
  539. return authFailure, nil, err
  540. }
  541. gotUserAuthInfoRequest = true
  542. // Manually unpack the prompt/echo pairs.
  543. rest := msg.Prompts
  544. var prompts []string
  545. var echos []bool
  546. for i := 0; i < int(msg.NumPrompts); i++ {
  547. prompt, r, ok := parseString(rest)
  548. if !ok || len(r) == 0 {
  549. return authFailure, nil, errors.New("ssh: prompt format error")
  550. }
  551. prompts = append(prompts, string(prompt))
  552. echos = append(echos, r[0] != 0)
  553. rest = r[1:]
  554. }
  555. if len(rest) != 0 {
  556. return authFailure, nil, errors.New("ssh: extra data following keyboard-interactive pairs")
  557. }
  558. answers, err := cb(msg.Name, msg.Instruction, prompts, echos)
  559. if err != nil {
  560. return authFailure, nil, err
  561. }
  562. if len(answers) != len(prompts) {
  563. return authFailure, nil, fmt.Errorf("ssh: incorrect number of answers from keyboard-interactive callback %d (expected %d)", len(answers), len(prompts))
  564. }
  565. responseLength := 1 + 4
  566. for _, a := range answers {
  567. responseLength += stringLength(len(a))
  568. }
  569. serialized := make([]byte, responseLength)
  570. p := serialized
  571. p[0] = msgUserAuthInfoResponse
  572. p = p[1:]
  573. p = marshalUint32(p, uint32(len(answers)))
  574. for _, a := range answers {
  575. p = marshalString(p, []byte(a))
  576. }
  577. if err := c.writePacket(serialized); err != nil {
  578. return authFailure, nil, err
  579. }
  580. }
  581. }
  582. type retryableAuthMethod struct {
  583. authMethod AuthMethod
  584. maxTries int
  585. }
  586. func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (ok authResult, methods []string, err error) {
  587. for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ {
  588. ok, methods, err = r.authMethod.auth(session, user, c, rand, extensions)
  589. if ok != authFailure || err != nil { // either success, partial success or error terminate
  590. return ok, methods, err
  591. }
  592. }
  593. return ok, methods, err
  594. }
  595. func (r *retryableAuthMethod) method() string {
  596. return r.authMethod.method()
  597. }
  598. // RetryableAuthMethod is a decorator for other auth methods enabling them to
  599. // be retried up to maxTries before considering that AuthMethod itself failed.
  600. // If maxTries is <= 0, will retry indefinitely
  601. //
  602. // This is useful for interactive clients using challenge/response type
  603. // authentication (e.g. Keyboard-Interactive, Password, etc) where the user
  604. // could mistype their response resulting in the server issuing a
  605. // SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4
  606. // [keyboard-interactive]); Without this decorator, the non-retryable
  607. // AuthMethod would be removed from future consideration, and never tried again
  608. // (and so the user would never be able to retry their entry).
  609. func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod {
  610. return &retryableAuthMethod{authMethod: auth, maxTries: maxTries}
  611. }
  612. // GSSAPIWithMICAuthMethod is an AuthMethod with "gssapi-with-mic" authentication.
  613. // See RFC 4462 section 3
  614. // gssAPIClient is implementation of the GSSAPIClient interface, see the definition of the interface for details.
  615. // target is the server host you want to log in to.
  616. func GSSAPIWithMICAuthMethod(gssAPIClient GSSAPIClient, target string) AuthMethod {
  617. if gssAPIClient == nil {
  618. panic("gss-api client must be not nil with enable gssapi-with-mic")
  619. }
  620. return &gssAPIWithMICCallback{gssAPIClient: gssAPIClient, target: target}
  621. }
  622. type gssAPIWithMICCallback struct {
  623. gssAPIClient GSSAPIClient
  624. target string
  625. }
  626. func (g *gssAPIWithMICCallback) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) {
  627. m := &userAuthRequestMsg{
  628. User: user,
  629. Service: serviceSSH,
  630. Method: g.method(),
  631. }
  632. // The GSS-API authentication method is initiated when the client sends an SSH_MSG_USERAUTH_REQUEST.
  633. // See RFC 4462 section 3.2.
  634. m.Payload = appendU32(m.Payload, 1)
  635. m.Payload = appendString(m.Payload, string(krb5OID))
  636. if err := c.writePacket(Marshal(m)); err != nil {
  637. return authFailure, nil, err
  638. }
  639. // The server responds to the SSH_MSG_USERAUTH_REQUEST with either an
  640. // SSH_MSG_USERAUTH_FAILURE if none of the mechanisms are supported or
  641. // with an SSH_MSG_USERAUTH_GSSAPI_RESPONSE.
  642. // See RFC 4462 section 3.3.
  643. // OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,so I don't want to check
  644. // selected mech if it is valid.
  645. packet, err := c.readPacket()
  646. if err != nil {
  647. return authFailure, nil, err
  648. }
  649. userAuthGSSAPIResp := &userAuthGSSAPIResponse{}
  650. if err := Unmarshal(packet, userAuthGSSAPIResp); err != nil {
  651. return authFailure, nil, err
  652. }
  653. // Start the loop into the exchange token.
  654. // See RFC 4462 section 3.4.
  655. var token []byte
  656. defer g.gssAPIClient.DeleteSecContext()
  657. for {
  658. // Initiates the establishment of a security context between the application and a remote peer.
  659. nextToken, needContinue, err := g.gssAPIClient.InitSecContext("host@"+g.target, token, false)
  660. if err != nil {
  661. return authFailure, nil, err
  662. }
  663. if len(nextToken) > 0 {
  664. if err := c.writePacket(Marshal(&userAuthGSSAPIToken{
  665. Token: nextToken,
  666. })); err != nil {
  667. return authFailure, nil, err
  668. }
  669. }
  670. if !needContinue {
  671. break
  672. }
  673. packet, err = c.readPacket()
  674. if err != nil {
  675. return authFailure, nil, err
  676. }
  677. switch packet[0] {
  678. case msgUserAuthFailure:
  679. var msg userAuthFailureMsg
  680. if err := Unmarshal(packet, &msg); err != nil {
  681. return authFailure, nil, err
  682. }
  683. if msg.PartialSuccess {
  684. return authPartialSuccess, msg.Methods, nil
  685. }
  686. return authFailure, msg.Methods, nil
  687. case msgUserAuthGSSAPIError:
  688. userAuthGSSAPIErrorResp := &userAuthGSSAPIError{}
  689. if err := Unmarshal(packet, userAuthGSSAPIErrorResp); err != nil {
  690. return authFailure, nil, err
  691. }
  692. return authFailure, nil, fmt.Errorf("GSS-API Error:\n"+
  693. "Major Status: %d\n"+
  694. "Minor Status: %d\n"+
  695. "Error Message: %s\n", userAuthGSSAPIErrorResp.MajorStatus, userAuthGSSAPIErrorResp.MinorStatus,
  696. userAuthGSSAPIErrorResp.Message)
  697. case msgUserAuthGSSAPIToken:
  698. userAuthGSSAPITokenReq := &userAuthGSSAPIToken{}
  699. if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
  700. return authFailure, nil, err
  701. }
  702. token = userAuthGSSAPITokenReq.Token
  703. }
  704. }
  705. // Binding Encryption Keys.
  706. // See RFC 4462 section 3.5.
  707. micField := buildMIC(string(session), user, "ssh-connection", "gssapi-with-mic")
  708. micToken, err := g.gssAPIClient.GetMIC(micField)
  709. if err != nil {
  710. return authFailure, nil, err
  711. }
  712. if err := c.writePacket(Marshal(&userAuthGSSAPIMIC{
  713. MIC: micToken,
  714. })); err != nil {
  715. return authFailure, nil, err
  716. }
  717. return handleAuthResponse(c)
  718. }
  719. func (g *gssAPIWithMICCallback) method() string {
  720. return "gssapi-with-mic"
  721. }