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