client_auth.go 21 KB

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