client_auth.go 23 KB

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