client_auth.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  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. for _, signer := range signers {
  274. pub := signer.PublicKey()
  275. as, algo, err := pickSignatureAlgorithm(signer, extensions)
  276. if err != nil && errSigAlgo == nil {
  277. // If we cannot negotiate a signature algorithm store the first
  278. // error so we can return it to provide a more meaningful message if
  279. // no other signers work.
  280. errSigAlgo = err
  281. continue
  282. }
  283. ok, err := validateKey(pub, algo, user, c)
  284. if err != nil {
  285. return authFailure, nil, err
  286. }
  287. if !ok {
  288. continue
  289. }
  290. pubKey := pub.Marshal()
  291. data := buildDataSignedForAuth(session, userAuthRequestMsg{
  292. User: user,
  293. Service: serviceSSH,
  294. Method: cb.method(),
  295. }, algo, pubKey)
  296. sign, err := as.SignWithAlgorithm(rand, data, underlyingAlgo(algo))
  297. if err != nil {
  298. return authFailure, nil, err
  299. }
  300. // manually wrap the serialized signature in a string
  301. s := Marshal(sign)
  302. sig := make([]byte, stringLength(len(s)))
  303. marshalString(sig, s)
  304. msg := publickeyAuthMsg{
  305. User: user,
  306. Service: serviceSSH,
  307. Method: cb.method(),
  308. HasSig: true,
  309. Algoname: algo,
  310. PubKey: pubKey,
  311. Sig: sig,
  312. }
  313. p := Marshal(&msg)
  314. if err := c.writePacket(p); err != nil {
  315. return authFailure, nil, err
  316. }
  317. var success authResult
  318. success, methods, err = handleAuthResponse(c)
  319. if err != nil {
  320. return authFailure, nil, err
  321. }
  322. // If authentication succeeds or the list of available methods does not
  323. // contain the "publickey" method, do not attempt to authenticate with any
  324. // other keys. According to RFC 4252 Section 7, the latter can occur when
  325. // additional authentication methods are required.
  326. if success == authSuccess || !contains(methods, cb.method()) {
  327. return success, methods, err
  328. }
  329. }
  330. return authFailure, methods, errSigAlgo
  331. }
  332. // validateKey validates the key provided is acceptable to the server.
  333. func validateKey(key PublicKey, algo string, user string, c packetConn) (bool, error) {
  334. pubKey := key.Marshal()
  335. msg := publickeyAuthMsg{
  336. User: user,
  337. Service: serviceSSH,
  338. Method: "publickey",
  339. HasSig: false,
  340. Algoname: algo,
  341. PubKey: pubKey,
  342. }
  343. if err := c.writePacket(Marshal(&msg)); err != nil {
  344. return false, err
  345. }
  346. return confirmKeyAck(key, algo, c)
  347. }
  348. func confirmKeyAck(key PublicKey, algo string, c packetConn) (bool, error) {
  349. pubKey := key.Marshal()
  350. for {
  351. packet, err := c.readPacket()
  352. if err != nil {
  353. return false, err
  354. }
  355. switch packet[0] {
  356. case msgUserAuthBanner:
  357. if err := handleBannerResponse(c, packet); err != nil {
  358. return false, err
  359. }
  360. case msgUserAuthPubKeyOk:
  361. var msg userAuthPubKeyOkMsg
  362. if err := Unmarshal(packet, &msg); err != nil {
  363. return false, err
  364. }
  365. if msg.Algo != algo || !bytes.Equal(msg.PubKey, pubKey) {
  366. return false, nil
  367. }
  368. return true, nil
  369. case msgUserAuthFailure:
  370. return false, nil
  371. default:
  372. return false, unexpectedMessageError(msgUserAuthPubKeyOk, packet[0])
  373. }
  374. }
  375. }
  376. // PublicKeys returns an AuthMethod that uses the given key
  377. // pairs.
  378. func PublicKeys(signers ...Signer) AuthMethod {
  379. return publicKeyCallback(func() ([]Signer, error) { return signers, nil })
  380. }
  381. // PublicKeysCallback returns an AuthMethod that runs the given
  382. // function to obtain a list of key pairs.
  383. func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMethod {
  384. return publicKeyCallback(getSigners)
  385. }
  386. // handleAuthResponse returns whether the preceding authentication request succeeded
  387. // along with a list of remaining authentication methods to try next and
  388. // an error if an unexpected response was received.
  389. func handleAuthResponse(c packetConn) (authResult, []string, error) {
  390. gotMsgExtInfo := false
  391. for {
  392. packet, err := c.readPacket()
  393. if err != nil {
  394. return authFailure, nil, err
  395. }
  396. switch packet[0] {
  397. case msgUserAuthBanner:
  398. if err := handleBannerResponse(c, packet); err != nil {
  399. return authFailure, nil, err
  400. }
  401. case msgExtInfo:
  402. // Ignore post-authentication RFC 8308 extensions, once.
  403. if gotMsgExtInfo {
  404. return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
  405. }
  406. gotMsgExtInfo = true
  407. case msgUserAuthFailure:
  408. var msg userAuthFailureMsg
  409. if err := Unmarshal(packet, &msg); err != nil {
  410. return authFailure, nil, err
  411. }
  412. if msg.PartialSuccess {
  413. return authPartialSuccess, msg.Methods, nil
  414. }
  415. return authFailure, msg.Methods, nil
  416. case msgUserAuthSuccess:
  417. return authSuccess, nil, nil
  418. default:
  419. return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
  420. }
  421. }
  422. }
  423. func handleBannerResponse(c packetConn, packet []byte) error {
  424. var msg userAuthBannerMsg
  425. if err := Unmarshal(packet, &msg); err != nil {
  426. return err
  427. }
  428. transport, ok := c.(*handshakeTransport)
  429. if !ok {
  430. return nil
  431. }
  432. if transport.bannerCallback != nil {
  433. return transport.bannerCallback(msg.Message)
  434. }
  435. return nil
  436. }
  437. // KeyboardInteractiveChallenge should print questions, optionally
  438. // disabling echoing (e.g. for passwords), and return all the answers.
  439. // Challenge may be called multiple times in a single session. After
  440. // successful authentication, the server may send a challenge with no
  441. // questions, for which the name and instruction messages should be
  442. // printed. RFC 4256 section 3.3 details how the UI should behave for
  443. // both CLI and GUI environments.
  444. type KeyboardInteractiveChallenge func(name, instruction string, questions []string, echos []bool) (answers []string, err error)
  445. // KeyboardInteractive returns an AuthMethod using a prompt/response
  446. // sequence controlled by the server.
  447. func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod {
  448. return challenge
  449. }
  450. func (cb KeyboardInteractiveChallenge) method() string {
  451. return "keyboard-interactive"
  452. }
  453. func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) {
  454. type initiateMsg struct {
  455. User string `sshtype:"50"`
  456. Service string
  457. Method string
  458. Language string
  459. Submethods string
  460. }
  461. if err := c.writePacket(Marshal(&initiateMsg{
  462. User: user,
  463. Service: serviceSSH,
  464. Method: "keyboard-interactive",
  465. })); err != nil {
  466. return authFailure, nil, err
  467. }
  468. gotMsgExtInfo := false
  469. for {
  470. packet, err := c.readPacket()
  471. if err != nil {
  472. return authFailure, nil, err
  473. }
  474. // like handleAuthResponse, but with less options.
  475. switch packet[0] {
  476. case msgUserAuthBanner:
  477. if err := handleBannerResponse(c, packet); err != nil {
  478. return authFailure, nil, err
  479. }
  480. continue
  481. case msgExtInfo:
  482. // Ignore post-authentication RFC 8308 extensions, once.
  483. if gotMsgExtInfo {
  484. return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
  485. }
  486. gotMsgExtInfo = true
  487. continue
  488. case msgUserAuthInfoRequest:
  489. // OK
  490. case msgUserAuthFailure:
  491. var msg userAuthFailureMsg
  492. if err := Unmarshal(packet, &msg); err != nil {
  493. return authFailure, nil, err
  494. }
  495. if msg.PartialSuccess {
  496. return authPartialSuccess, msg.Methods, nil
  497. }
  498. return authFailure, msg.Methods, nil
  499. case msgUserAuthSuccess:
  500. return authSuccess, nil, nil
  501. default:
  502. return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
  503. }
  504. var msg userAuthInfoRequestMsg
  505. if err := Unmarshal(packet, &msg); err != nil {
  506. return authFailure, nil, err
  507. }
  508. // Manually unpack the prompt/echo pairs.
  509. rest := msg.Prompts
  510. var prompts []string
  511. var echos []bool
  512. for i := 0; i < int(msg.NumPrompts); i++ {
  513. prompt, r, ok := parseString(rest)
  514. if !ok || len(r) == 0 {
  515. return authFailure, nil, errors.New("ssh: prompt format error")
  516. }
  517. prompts = append(prompts, string(prompt))
  518. echos = append(echos, r[0] != 0)
  519. rest = r[1:]
  520. }
  521. if len(rest) != 0 {
  522. return authFailure, nil, errors.New("ssh: extra data following keyboard-interactive pairs")
  523. }
  524. answers, err := cb(msg.Name, msg.Instruction, prompts, echos)
  525. if err != nil {
  526. return authFailure, nil, err
  527. }
  528. if len(answers) != len(prompts) {
  529. return authFailure, nil, fmt.Errorf("ssh: incorrect number of answers from keyboard-interactive callback %d (expected %d)", len(answers), len(prompts))
  530. }
  531. responseLength := 1 + 4
  532. for _, a := range answers {
  533. responseLength += stringLength(len(a))
  534. }
  535. serialized := make([]byte, responseLength)
  536. p := serialized
  537. p[0] = msgUserAuthInfoResponse
  538. p = p[1:]
  539. p = marshalUint32(p, uint32(len(answers)))
  540. for _, a := range answers {
  541. p = marshalString(p, []byte(a))
  542. }
  543. if err := c.writePacket(serialized); err != nil {
  544. return authFailure, nil, err
  545. }
  546. }
  547. }
  548. type retryableAuthMethod struct {
  549. authMethod AuthMethod
  550. maxTries int
  551. }
  552. func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (ok authResult, methods []string, err error) {
  553. for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ {
  554. ok, methods, err = r.authMethod.auth(session, user, c, rand, extensions)
  555. if ok != authFailure || err != nil { // either success, partial success or error terminate
  556. return ok, methods, err
  557. }
  558. }
  559. return ok, methods, err
  560. }
  561. func (r *retryableAuthMethod) method() string {
  562. return r.authMethod.method()
  563. }
  564. // RetryableAuthMethod is a decorator for other auth methods enabling them to
  565. // be retried up to maxTries before considering that AuthMethod itself failed.
  566. // If maxTries is <= 0, will retry indefinitely
  567. //
  568. // This is useful for interactive clients using challenge/response type
  569. // authentication (e.g. Keyboard-Interactive, Password, etc) where the user
  570. // could mistype their response resulting in the server issuing a
  571. // SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4
  572. // [keyboard-interactive]); Without this decorator, the non-retryable
  573. // AuthMethod would be removed from future consideration, and never tried again
  574. // (and so the user would never be able to retry their entry).
  575. func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod {
  576. return &retryableAuthMethod{authMethod: auth, maxTries: maxTries}
  577. }
  578. // GSSAPIWithMICAuthMethod is an AuthMethod with "gssapi-with-mic" authentication.
  579. // See RFC 4462 section 3
  580. // gssAPIClient is implementation of the GSSAPIClient interface, see the definition of the interface for details.
  581. // target is the server host you want to log in to.
  582. func GSSAPIWithMICAuthMethod(gssAPIClient GSSAPIClient, target string) AuthMethod {
  583. if gssAPIClient == nil {
  584. panic("gss-api client must be not nil with enable gssapi-with-mic")
  585. }
  586. return &gssAPIWithMICCallback{gssAPIClient: gssAPIClient, target: target}
  587. }
  588. type gssAPIWithMICCallback struct {
  589. gssAPIClient GSSAPIClient
  590. target string
  591. }
  592. func (g *gssAPIWithMICCallback) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) {
  593. m := &userAuthRequestMsg{
  594. User: user,
  595. Service: serviceSSH,
  596. Method: g.method(),
  597. }
  598. // The GSS-API authentication method is initiated when the client sends an SSH_MSG_USERAUTH_REQUEST.
  599. // See RFC 4462 section 3.2.
  600. m.Payload = appendU32(m.Payload, 1)
  601. m.Payload = appendString(m.Payload, string(krb5OID))
  602. if err := c.writePacket(Marshal(m)); err != nil {
  603. return authFailure, nil, err
  604. }
  605. // The server responds to the SSH_MSG_USERAUTH_REQUEST with either an
  606. // SSH_MSG_USERAUTH_FAILURE if none of the mechanisms are supported or
  607. // with an SSH_MSG_USERAUTH_GSSAPI_RESPONSE.
  608. // See RFC 4462 section 3.3.
  609. // OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,so I don't want to check
  610. // selected mech if it is valid.
  611. packet, err := c.readPacket()
  612. if err != nil {
  613. return authFailure, nil, err
  614. }
  615. userAuthGSSAPIResp := &userAuthGSSAPIResponse{}
  616. if err := Unmarshal(packet, userAuthGSSAPIResp); err != nil {
  617. return authFailure, nil, err
  618. }
  619. // Start the loop into the exchange token.
  620. // See RFC 4462 section 3.4.
  621. var token []byte
  622. defer g.gssAPIClient.DeleteSecContext()
  623. for {
  624. // Initiates the establishment of a security context between the application and a remote peer.
  625. nextToken, needContinue, err := g.gssAPIClient.InitSecContext("host@"+g.target, token, false)
  626. if err != nil {
  627. return authFailure, nil, err
  628. }
  629. if len(nextToken) > 0 {
  630. if err := c.writePacket(Marshal(&userAuthGSSAPIToken{
  631. Token: nextToken,
  632. })); err != nil {
  633. return authFailure, nil, err
  634. }
  635. }
  636. if !needContinue {
  637. break
  638. }
  639. packet, err = c.readPacket()
  640. if err != nil {
  641. return authFailure, nil, err
  642. }
  643. switch packet[0] {
  644. case msgUserAuthFailure:
  645. var msg userAuthFailureMsg
  646. if err := Unmarshal(packet, &msg); err != nil {
  647. return authFailure, nil, err
  648. }
  649. if msg.PartialSuccess {
  650. return authPartialSuccess, msg.Methods, nil
  651. }
  652. return authFailure, msg.Methods, nil
  653. case msgUserAuthGSSAPIError:
  654. userAuthGSSAPIErrorResp := &userAuthGSSAPIError{}
  655. if err := Unmarshal(packet, userAuthGSSAPIErrorResp); err != nil {
  656. return authFailure, nil, err
  657. }
  658. return authFailure, nil, fmt.Errorf("GSS-API Error:\n"+
  659. "Major Status: %d\n"+
  660. "Minor Status: %d\n"+
  661. "Error Message: %s\n", userAuthGSSAPIErrorResp.MajorStatus, userAuthGSSAPIErrorResp.MinorStatus,
  662. userAuthGSSAPIErrorResp.Message)
  663. case msgUserAuthGSSAPIToken:
  664. userAuthGSSAPITokenReq := &userAuthGSSAPIToken{}
  665. if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
  666. return authFailure, nil, err
  667. }
  668. token = userAuthGSSAPITokenReq.Token
  669. }
  670. }
  671. // Binding Encryption Keys.
  672. // See RFC 4462 section 3.5.
  673. micField := buildMIC(string(session), user, "ssh-connection", "gssapi-with-mic")
  674. micToken, err := g.gssAPIClient.GetMIC(micField)
  675. if err != nil {
  676. return authFailure, nil, err
  677. }
  678. if err := c.writePacket(Marshal(&userAuthGSSAPIMIC{
  679. MIC: micToken,
  680. })); err != nil {
  681. return authFailure, nil, err
  682. }
  683. return handleAuthResponse(c)
  684. }
  685. func (g *gssAPIWithMICCallback) method() string {
  686. return "gssapi-with-mic"
  687. }