a geicko-2 based round robin ranking system designed to test c++ battleship submissions
battleship.dunkirk.sh
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
5package ssh
6
7import (
8 "bytes"
9 "errors"
10 "fmt"
11 "io"
12 "net"
13 "strings"
14)
15
16// The Permissions type holds fine-grained permissions that are
17// specific to a user or a specific authentication method for a user.
18// The Permissions value for a successful authentication attempt is
19// available in ServerConn, so it can be used to pass information from
20// the user-authentication phase to the application layer.
21type Permissions struct {
22 // CriticalOptions indicate restrictions to the default
23 // permissions, and are typically used in conjunction with
24 // user certificates. The standard for SSH certificates
25 // defines "force-command" (only allow the given command to
26 // execute) and "source-address" (only allow connections from
27 // the given address). The SSH package currently only enforces
28 // the "source-address" critical option. It is up to server
29 // implementations to enforce other critical options, such as
30 // "force-command", by checking them after the SSH handshake
31 // is successful. In general, SSH servers should reject
32 // connections that specify critical options that are unknown
33 // or not supported.
34 CriticalOptions map[string]string
35
36 // Extensions are extra functionality that the server may
37 // offer on authenticated connections. Lack of support for an
38 // extension does not preclude authenticating a user. Common
39 // extensions are "permit-agent-forwarding",
40 // "permit-X11-forwarding". The Go SSH library currently does
41 // not act on any extension, and it is up to server
42 // implementations to honor them. Extensions can be used to
43 // pass data from the authentication callbacks to the server
44 // application layer.
45 Extensions map[string]string
46}
47
48type GSSAPIWithMICConfig struct {
49 // AllowLogin, must be set, is called when gssapi-with-mic
50 // authentication is selected (RFC 4462 section 3). The srcName is from the
51 // results of the GSS-API authentication. The format is username@DOMAIN.
52 // GSSAPI just guarantees to the server who the user is, but not if they can log in, and with what permissions.
53 // This callback is called after the user identity is established with GSSAPI to decide if the user can login with
54 // which permissions. If the user is allowed to login, it should return a nil error.
55 AllowLogin func(conn ConnMetadata, srcName string) (*Permissions, error)
56
57 // Server must be set. It's the implementation
58 // of the GSSAPIServer interface. See GSSAPIServer interface for details.
59 Server GSSAPIServer
60}
61
62// SendAuthBanner implements [ServerPreAuthConn].
63func (s *connection) SendAuthBanner(msg string) error {
64 return s.transport.writePacket(Marshal(&userAuthBannerMsg{
65 Message: msg,
66 }))
67}
68
69func (*connection) unexportedMethodForFutureProofing() {}
70
71// ServerPreAuthConn is the interface available on an incoming server
72// connection before authentication has completed.
73type ServerPreAuthConn interface {
74 unexportedMethodForFutureProofing() // permits growing ServerPreAuthConn safely later, ala testing.TB
75
76 ConnMetadata
77
78 // SendAuthBanner sends a banner message to the client.
79 // It returns an error once the authentication phase has ended.
80 SendAuthBanner(string) error
81}
82
83// ServerConfig holds server specific configuration data.
84type ServerConfig struct {
85 // Config contains configuration shared between client and server.
86 Config
87
88 // PublicKeyAuthAlgorithms specifies the supported client public key
89 // authentication algorithms. Note that this should not include certificate
90 // types since those use the underlying algorithm. This list is sent to the
91 // client if it supports the server-sig-algs extension. Order is irrelevant.
92 // If unspecified then a default set of algorithms is used.
93 PublicKeyAuthAlgorithms []string
94
95 hostKeys []Signer
96
97 // NoClientAuth is true if clients are allowed to connect without
98 // authenticating.
99 // To determine NoClientAuth at runtime, set NoClientAuth to true
100 // and the optional NoClientAuthCallback to a non-nil value.
101 NoClientAuth bool
102
103 // NoClientAuthCallback, if non-nil, is called when a user
104 // attempts to authenticate with auth method "none".
105 // NoClientAuth must also be set to true for this be used, or
106 // this func is unused.
107 NoClientAuthCallback func(ConnMetadata) (*Permissions, error)
108
109 // MaxAuthTries specifies the maximum number of authentication attempts
110 // permitted per connection. If set to a negative number, the number of
111 // attempts are unlimited. If set to zero, the number of attempts are limited
112 // to 6.
113 MaxAuthTries int
114
115 // PasswordCallback, if non-nil, is called when a user
116 // attempts to authenticate using a password.
117 PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
118
119 // PublicKeyCallback, if non-nil, is called when a client
120 // offers a public key for authentication. It must return a nil error
121 // if the given public key can be used to authenticate the
122 // given user. For example, see CertChecker.Authenticate. A
123 // call to this function does not guarantee that the key
124 // offered is in fact used to authenticate. To record any data
125 // depending on the public key, store it inside a
126 // Permissions.Extensions entry.
127 PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
128
129 // KeyboardInteractiveCallback, if non-nil, is called when
130 // keyboard-interactive authentication is selected (RFC
131 // 4256). The client object's Challenge function should be
132 // used to query the user. The callback may offer multiple
133 // Challenge rounds. To avoid information leaks, the client
134 // should be presented a challenge even if the user is
135 // unknown.
136 KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
137
138 // AuthLogCallback, if non-nil, is called to log all authentication
139 // attempts.
140 AuthLogCallback func(conn ConnMetadata, method string, err error)
141
142 // PreAuthConnCallback, if non-nil, is called upon receiving a new connection
143 // before any authentication has started. The provided ServerPreAuthConn
144 // can be used at any time before authentication is complete, including
145 // after this callback has returned.
146 PreAuthConnCallback func(ServerPreAuthConn)
147
148 // ServerVersion is the version identification string to announce in
149 // the public handshake.
150 // If empty, a reasonable default is used.
151 // Note that RFC 4253 section 4.2 requires that this string start with
152 // "SSH-2.0-".
153 ServerVersion string
154
155 // BannerCallback, if present, is called and the return string is sent to
156 // the client after key exchange completed but before authentication.
157 BannerCallback func(conn ConnMetadata) string
158
159 // GSSAPIWithMICConfig includes gssapi server and callback, which if both non-nil, is used
160 // when gssapi-with-mic authentication is selected (RFC 4462 section 3).
161 GSSAPIWithMICConfig *GSSAPIWithMICConfig
162}
163
164// AddHostKey adds a private key as a host key. If an existing host
165// key exists with the same public key format, it is replaced. Each server
166// config must have at least one host key.
167func (s *ServerConfig) AddHostKey(key Signer) {
168 for i, k := range s.hostKeys {
169 if k.PublicKey().Type() == key.PublicKey().Type() {
170 s.hostKeys[i] = key
171 return
172 }
173 }
174
175 s.hostKeys = append(s.hostKeys, key)
176}
177
178// cachedPubKey contains the results of querying whether a public key is
179// acceptable for a user. This is a FIFO cache.
180type cachedPubKey struct {
181 user string
182 pubKeyData []byte
183 result error
184 perms *Permissions
185}
186
187// maxCachedPubKeys is the number of cache entries we store.
188//
189// Due to consistent misuse of the PublicKeyCallback API, we have reduced this
190// to 1, such that the only key in the cache is the most recently seen one. This
191// forces the behavior that the last call to PublicKeyCallback will always be
192// with the key that is used for authentication.
193const maxCachedPubKeys = 1
194
195// pubKeyCache caches tests for public keys. Since SSH clients
196// will query whether a public key is acceptable before attempting to
197// authenticate with it, we end up with duplicate queries for public
198// key validity. The cache only applies to a single ServerConn.
199type pubKeyCache struct {
200 keys []cachedPubKey
201}
202
203// get returns the result for a given user/algo/key tuple.
204func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) {
205 for _, k := range c.keys {
206 if k.user == user && bytes.Equal(k.pubKeyData, pubKeyData) {
207 return k, true
208 }
209 }
210 return cachedPubKey{}, false
211}
212
213// add adds the given tuple to the cache.
214func (c *pubKeyCache) add(candidate cachedPubKey) {
215 if len(c.keys) >= maxCachedPubKeys {
216 c.keys = c.keys[1:]
217 }
218 c.keys = append(c.keys, candidate)
219}
220
221// ServerConn is an authenticated SSH connection, as seen from the
222// server
223type ServerConn struct {
224 Conn
225
226 // If the succeeding authentication callback returned a
227 // non-nil Permissions pointer, it is stored here.
228 Permissions *Permissions
229}
230
231// NewServerConn starts a new SSH server with c as the underlying
232// transport. It starts with a handshake and, if the handshake is
233// unsuccessful, it closes the connection and returns an error. The
234// Request and NewChannel channels must be serviced, or the connection
235// will hang.
236//
237// The returned error may be of type *ServerAuthError for
238// authentication errors.
239func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
240 fullConf := *config
241 fullConf.SetDefaults()
242 if fullConf.MaxAuthTries == 0 {
243 fullConf.MaxAuthTries = 6
244 }
245 if len(fullConf.PublicKeyAuthAlgorithms) == 0 {
246 fullConf.PublicKeyAuthAlgorithms = defaultPubKeyAuthAlgos
247 } else {
248 for _, algo := range fullConf.PublicKeyAuthAlgorithms {
249 if !contains(SupportedAlgorithms().PublicKeyAuths, algo) && !contains(InsecureAlgorithms().PublicKeyAuths, algo) {
250 c.Close()
251 return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo)
252 }
253 }
254 }
255
256 s := &connection{
257 sshConn: sshConn{conn: c},
258 }
259 perms, err := s.serverHandshake(&fullConf)
260 if err != nil {
261 c.Close()
262 return nil, nil, nil, err
263 }
264 return &ServerConn{s, perms}, s.mux.incomingChannels, s.mux.incomingRequests, nil
265}
266
267// signAndMarshal signs the data with the appropriate algorithm,
268// and serializes the result in SSH wire format. algo is the negotiate
269// algorithm and may be a certificate type.
270func signAndMarshal(k AlgorithmSigner, rand io.Reader, data []byte, algo string) ([]byte, error) {
271 sig, err := k.SignWithAlgorithm(rand, data, underlyingAlgo(algo))
272 if err != nil {
273 return nil, err
274 }
275
276 return Marshal(sig), nil
277}
278
279// handshake performs key exchange and user authentication.
280func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) {
281 if len(config.hostKeys) == 0 {
282 return nil, errors.New("ssh: server has no host keys")
283 }
284
285 if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil &&
286 config.KeyboardInteractiveCallback == nil && (config.GSSAPIWithMICConfig == nil ||
287 config.GSSAPIWithMICConfig.AllowLogin == nil || config.GSSAPIWithMICConfig.Server == nil) {
288 return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
289 }
290
291 if config.ServerVersion != "" {
292 s.serverVersion = []byte(config.ServerVersion)
293 } else {
294 s.serverVersion = []byte(packageVersion)
295 }
296 var err error
297 s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion)
298 if err != nil {
299 return nil, err
300 }
301
302 tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
303 s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config)
304
305 if err := s.transport.waitSession(); err != nil {
306 return nil, err
307 }
308
309 // We just did the key change, so the session ID is established.
310 s.sessionID = s.transport.getSessionID()
311 s.algorithms = s.transport.getAlgorithms()
312
313 var packet []byte
314 if packet, err = s.transport.readPacket(); err != nil {
315 return nil, err
316 }
317
318 var serviceRequest serviceRequestMsg
319 if err = Unmarshal(packet, &serviceRequest); err != nil {
320 return nil, err
321 }
322 if serviceRequest.Service != serviceUserAuth {
323 return nil, errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating")
324 }
325 serviceAccept := serviceAcceptMsg{
326 Service: serviceUserAuth,
327 }
328 if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil {
329 return nil, err
330 }
331
332 perms, err := s.serverAuthenticate(config)
333 if err != nil {
334 return nil, err
335 }
336 s.mux = newMux(s.transport)
337 return perms, err
338}
339
340func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
341 if addr == nil {
342 return errors.New("ssh: no address known for client, but source-address match required")
343 }
344
345 tcpAddr, ok := addr.(*net.TCPAddr)
346 if !ok {
347 return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr)
348 }
349
350 for _, sourceAddr := range strings.Split(sourceAddrs, ",") {
351 if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil {
352 if allowedIP.Equal(tcpAddr.IP) {
353 return nil
354 }
355 } else {
356 _, ipNet, err := net.ParseCIDR(sourceAddr)
357 if err != nil {
358 return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err)
359 }
360
361 if ipNet.Contains(tcpAddr.IP) {
362 return nil
363 }
364 }
365 }
366
367 return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
368}
369
370func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, token []byte, s *connection,
371 sessionID []byte, userAuthReq userAuthRequestMsg) (authErr error, perms *Permissions, err error) {
372 gssAPIServer := gssapiConfig.Server
373 defer gssAPIServer.DeleteSecContext()
374 var srcName string
375 for {
376 var (
377 outToken []byte
378 needContinue bool
379 )
380 outToken, srcName, needContinue, err = gssAPIServer.AcceptSecContext(token)
381 if err != nil {
382 return err, nil, nil
383 }
384 if len(outToken) != 0 {
385 if err := s.transport.writePacket(Marshal(&userAuthGSSAPIToken{
386 Token: outToken,
387 })); err != nil {
388 return nil, nil, err
389 }
390 }
391 if !needContinue {
392 break
393 }
394 packet, err := s.transport.readPacket()
395 if err != nil {
396 return nil, nil, err
397 }
398 userAuthGSSAPITokenReq := &userAuthGSSAPIToken{}
399 if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
400 return nil, nil, err
401 }
402 token = userAuthGSSAPITokenReq.Token
403 }
404 packet, err := s.transport.readPacket()
405 if err != nil {
406 return nil, nil, err
407 }
408 userAuthGSSAPIMICReq := &userAuthGSSAPIMIC{}
409 if err := Unmarshal(packet, userAuthGSSAPIMICReq); err != nil {
410 return nil, nil, err
411 }
412 mic := buildMIC(string(sessionID), userAuthReq.User, userAuthReq.Service, userAuthReq.Method)
413 if err := gssAPIServer.VerifyMIC(mic, userAuthGSSAPIMICReq.MIC); err != nil {
414 return err, nil, nil
415 }
416 perms, authErr = gssapiConfig.AllowLogin(s, srcName)
417 return authErr, perms, nil
418}
419
420// isAlgoCompatible checks if the signature format is compatible with the
421// selected algorithm taking into account edge cases that occur with old
422// clients.
423func isAlgoCompatible(algo, sigFormat string) bool {
424 // Compatibility for old clients.
425 //
426 // For certificate authentication with OpenSSH 7.2-7.7 signature format can
427 // be rsa-sha2-256 or rsa-sha2-512 for the algorithm
428 // ssh-rsa-cert-v01@openssh.com.
429 //
430 // With gpg-agent < 2.2.6 the algorithm can be rsa-sha2-256 or rsa-sha2-512
431 // for signature format ssh-rsa.
432 if isRSA(algo) && isRSA(sigFormat) {
433 return true
434 }
435 // Standard case: the underlying algorithm must match the signature format.
436 return underlyingAlgo(algo) == sigFormat
437}
438
439// ServerAuthError represents server authentication errors and is
440// sometimes returned by NewServerConn. It appends any authentication
441// errors that may occur, and is returned if all of the authentication
442// methods provided by the user failed to authenticate.
443type ServerAuthError struct {
444 // Errors contains authentication errors returned by the authentication
445 // callback methods. The first entry is typically ErrNoAuth.
446 Errors []error
447}
448
449func (l ServerAuthError) Error() string {
450 var errs []string
451 for _, err := range l.Errors {
452 errs = append(errs, err.Error())
453 }
454 return "[" + strings.Join(errs, ", ") + "]"
455}
456
457// ServerAuthCallbacks defines server-side authentication callbacks.
458type ServerAuthCallbacks struct {
459 // PasswordCallback behaves like [ServerConfig.PasswordCallback].
460 PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
461
462 // PublicKeyCallback behaves like [ServerConfig.PublicKeyCallback].
463 PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
464
465 // KeyboardInteractiveCallback behaves like [ServerConfig.KeyboardInteractiveCallback].
466 KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
467
468 // GSSAPIWithMICConfig behaves like [ServerConfig.GSSAPIWithMICConfig].
469 GSSAPIWithMICConfig *GSSAPIWithMICConfig
470}
471
472// PartialSuccessError can be returned by any of the [ServerConfig]
473// authentication callbacks to indicate to the client that authentication has
474// partially succeeded, but further steps are required.
475type PartialSuccessError struct {
476 // Next defines the authentication callbacks to apply to further steps. The
477 // available methods communicated to the client are based on the non-nil
478 // ServerAuthCallbacks fields.
479 Next ServerAuthCallbacks
480}
481
482func (p *PartialSuccessError) Error() string {
483 return "ssh: authenticated with partial success"
484}
485
486// ErrNoAuth is the error value returned if no
487// authentication method has been passed yet. This happens as a normal
488// part of the authentication loop, since the client first tries
489// 'none' authentication to discover available methods.
490// It is returned in ServerAuthError.Errors from NewServerConn.
491var ErrNoAuth = errors.New("ssh: no auth passed yet")
492
493// BannerError is an error that can be returned by authentication handlers in
494// ServerConfig to send a banner message to the client.
495type BannerError struct {
496 Err error
497 Message string
498}
499
500func (b *BannerError) Unwrap() error {
501 return b.Err
502}
503
504func (b *BannerError) Error() string {
505 if b.Err == nil {
506 return b.Message
507 }
508 return b.Err.Error()
509}
510
511func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
512 if config.PreAuthConnCallback != nil {
513 config.PreAuthConnCallback(s)
514 }
515
516 sessionID := s.transport.getSessionID()
517 var cache pubKeyCache
518 var perms *Permissions
519
520 authFailures := 0
521 noneAuthCount := 0
522 var authErrs []error
523 var calledBannerCallback bool
524 partialSuccessReturned := false
525 // Set the initial authentication callbacks from the config. They can be
526 // changed if a PartialSuccessError is returned.
527 authConfig := ServerAuthCallbacks{
528 PasswordCallback: config.PasswordCallback,
529 PublicKeyCallback: config.PublicKeyCallback,
530 KeyboardInteractiveCallback: config.KeyboardInteractiveCallback,
531 GSSAPIWithMICConfig: config.GSSAPIWithMICConfig,
532 }
533
534userAuthLoop:
535 for {
536 if authFailures >= config.MaxAuthTries && config.MaxAuthTries > 0 {
537 discMsg := &disconnectMsg{
538 Reason: 2,
539 Message: "too many authentication failures",
540 }
541
542 if err := s.transport.writePacket(Marshal(discMsg)); err != nil {
543 return nil, err
544 }
545 authErrs = append(authErrs, discMsg)
546 return nil, &ServerAuthError{Errors: authErrs}
547 }
548
549 var userAuthReq userAuthRequestMsg
550 if packet, err := s.transport.readPacket(); err != nil {
551 if err == io.EOF {
552 return nil, &ServerAuthError{Errors: authErrs}
553 }
554 return nil, err
555 } else if err = Unmarshal(packet, &userAuthReq); err != nil {
556 return nil, err
557 }
558
559 if userAuthReq.Service != serviceSSH {
560 return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service)
561 }
562
563 if s.user != userAuthReq.User && partialSuccessReturned {
564 return nil, fmt.Errorf("ssh: client changed the user after a partial success authentication, previous user %q, current user %q",
565 s.user, userAuthReq.User)
566 }
567
568 s.user = userAuthReq.User
569
570 if !calledBannerCallback && config.BannerCallback != nil {
571 calledBannerCallback = true
572 if msg := config.BannerCallback(s); msg != "" {
573 if err := s.SendAuthBanner(msg); err != nil {
574 return nil, err
575 }
576 }
577 }
578
579 perms = nil
580 authErr := ErrNoAuth
581
582 switch userAuthReq.Method {
583 case "none":
584 noneAuthCount++
585 // We don't allow none authentication after a partial success
586 // response.
587 if config.NoClientAuth && !partialSuccessReturned {
588 if config.NoClientAuthCallback != nil {
589 perms, authErr = config.NoClientAuthCallback(s)
590 } else {
591 authErr = nil
592 }
593 }
594 case "password":
595 if authConfig.PasswordCallback == nil {
596 authErr = errors.New("ssh: password auth not configured")
597 break
598 }
599 payload := userAuthReq.Payload
600 if len(payload) < 1 || payload[0] != 0 {
601 return nil, parseError(msgUserAuthRequest)
602 }
603 payload = payload[1:]
604 password, payload, ok := parseString(payload)
605 if !ok || len(payload) > 0 {
606 return nil, parseError(msgUserAuthRequest)
607 }
608
609 perms, authErr = authConfig.PasswordCallback(s, password)
610 case "keyboard-interactive":
611 if authConfig.KeyboardInteractiveCallback == nil {
612 authErr = errors.New("ssh: keyboard-interactive auth not configured")
613 break
614 }
615
616 prompter := &sshClientKeyboardInteractive{s}
617 perms, authErr = authConfig.KeyboardInteractiveCallback(s, prompter.Challenge)
618 case "publickey":
619 if authConfig.PublicKeyCallback == nil {
620 authErr = errors.New("ssh: publickey auth not configured")
621 break
622 }
623 payload := userAuthReq.Payload
624 if len(payload) < 1 {
625 return nil, parseError(msgUserAuthRequest)
626 }
627 isQuery := payload[0] == 0
628 payload = payload[1:]
629 algoBytes, payload, ok := parseString(payload)
630 if !ok {
631 return nil, parseError(msgUserAuthRequest)
632 }
633 algo := string(algoBytes)
634 if !contains(config.PublicKeyAuthAlgorithms, underlyingAlgo(algo)) {
635 authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
636 break
637 }
638
639 pubKeyData, payload, ok := parseString(payload)
640 if !ok {
641 return nil, parseError(msgUserAuthRequest)
642 }
643
644 pubKey, err := ParsePublicKey(pubKeyData)
645 if err != nil {
646 return nil, err
647 }
648
649 candidate, ok := cache.get(s.user, pubKeyData)
650 if !ok {
651 candidate.user = s.user
652 candidate.pubKeyData = pubKeyData
653 candidate.perms, candidate.result = authConfig.PublicKeyCallback(s, pubKey)
654 _, isPartialSuccessError := candidate.result.(*PartialSuccessError)
655
656 if (candidate.result == nil || isPartialSuccessError) &&
657 candidate.perms != nil &&
658 candidate.perms.CriticalOptions != nil &&
659 candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" {
660 if err := checkSourceAddress(
661 s.RemoteAddr(),
662 candidate.perms.CriticalOptions[sourceAddressCriticalOption]); err != nil {
663 candidate.result = err
664 }
665 }
666 cache.add(candidate)
667 }
668
669 if isQuery {
670 // The client can query if the given public key
671 // would be okay.
672
673 if len(payload) > 0 {
674 return nil, parseError(msgUserAuthRequest)
675 }
676 _, isPartialSuccessError := candidate.result.(*PartialSuccessError)
677 if candidate.result == nil || isPartialSuccessError {
678 okMsg := userAuthPubKeyOkMsg{
679 Algo: algo,
680 PubKey: pubKeyData,
681 }
682 if err = s.transport.writePacket(Marshal(&okMsg)); err != nil {
683 return nil, err
684 }
685 continue userAuthLoop
686 }
687 authErr = candidate.result
688 } else {
689 sig, payload, ok := parseSignature(payload)
690 if !ok || len(payload) > 0 {
691 return nil, parseError(msgUserAuthRequest)
692 }
693 // Ensure the declared public key algo is compatible with the
694 // decoded one. This check will ensure we don't accept e.g.
695 // ssh-rsa-cert-v01@openssh.com algorithm with ssh-rsa public
696 // key type. The algorithm and public key type must be
697 // consistent: both must be certificate algorithms, or neither.
698 if !contains(algorithmsForKeyFormat(pubKey.Type()), algo) {
699 authErr = fmt.Errorf("ssh: public key type %q not compatible with selected algorithm %q",
700 pubKey.Type(), algo)
701 break
702 }
703 // Ensure the public key algo and signature algo
704 // are supported. Compare the private key
705 // algorithm name that corresponds to algo with
706 // sig.Format. This is usually the same, but
707 // for certs, the names differ.
708 if !contains(config.PublicKeyAuthAlgorithms, sig.Format) {
709 authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
710 break
711 }
712 if !isAlgoCompatible(algo, sig.Format) {
713 authErr = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", sig.Format, algo)
714 break
715 }
716
717 signedData := buildDataSignedForAuth(sessionID, userAuthReq, algo, pubKeyData)
718
719 if err := pubKey.Verify(signedData, sig); err != nil {
720 return nil, err
721 }
722
723 authErr = candidate.result
724 perms = candidate.perms
725 }
726 case "gssapi-with-mic":
727 if authConfig.GSSAPIWithMICConfig == nil {
728 authErr = errors.New("ssh: gssapi-with-mic auth not configured")
729 break
730 }
731 gssapiConfig := authConfig.GSSAPIWithMICConfig
732 userAuthRequestGSSAPI, err := parseGSSAPIPayload(userAuthReq.Payload)
733 if err != nil {
734 return nil, parseError(msgUserAuthRequest)
735 }
736 // OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication.
737 if userAuthRequestGSSAPI.N == 0 {
738 authErr = fmt.Errorf("ssh: Mechanism negotiation is not supported")
739 break
740 }
741 var i uint32
742 present := false
743 for i = 0; i < userAuthRequestGSSAPI.N; i++ {
744 if userAuthRequestGSSAPI.OIDS[i].Equal(krb5Mesh) {
745 present = true
746 break
747 }
748 }
749 if !present {
750 authErr = fmt.Errorf("ssh: GSSAPI authentication must use the Kerberos V5 mechanism")
751 break
752 }
753 // Initial server response, see RFC 4462 section 3.3.
754 if err := s.transport.writePacket(Marshal(&userAuthGSSAPIResponse{
755 SupportMech: krb5OID,
756 })); err != nil {
757 return nil, err
758 }
759 // Exchange token, see RFC 4462 section 3.4.
760 packet, err := s.transport.readPacket()
761 if err != nil {
762 return nil, err
763 }
764 userAuthGSSAPITokenReq := &userAuthGSSAPIToken{}
765 if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
766 return nil, err
767 }
768 authErr, perms, err = gssExchangeToken(gssapiConfig, userAuthGSSAPITokenReq.Token, s, sessionID,
769 userAuthReq)
770 if err != nil {
771 return nil, err
772 }
773 default:
774 authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method)
775 }
776
777 authErrs = append(authErrs, authErr)
778
779 if config.AuthLogCallback != nil {
780 config.AuthLogCallback(s, userAuthReq.Method, authErr)
781 }
782
783 var bannerErr *BannerError
784 if errors.As(authErr, &bannerErr) {
785 if bannerErr.Message != "" {
786 if err := s.SendAuthBanner(bannerErr.Message); err != nil {
787 return nil, err
788 }
789 }
790 }
791
792 if authErr == nil {
793 break userAuthLoop
794 }
795
796 var failureMsg userAuthFailureMsg
797
798 if partialSuccess, ok := authErr.(*PartialSuccessError); ok {
799 // After a partial success error we don't allow changing the user
800 // name and execute the NoClientAuthCallback.
801 partialSuccessReturned = true
802
803 // In case a partial success is returned, the server may send
804 // a new set of authentication methods.
805 authConfig = partialSuccess.Next
806
807 // Reset pubkey cache, as the new PublicKeyCallback might
808 // accept a different set of public keys.
809 cache = pubKeyCache{}
810
811 // Send back a partial success message to the user.
812 failureMsg.PartialSuccess = true
813 } else {
814 // Allow initial attempt of 'none' without penalty.
815 if authFailures > 0 || userAuthReq.Method != "none" || noneAuthCount != 1 {
816 authFailures++
817 }
818 if config.MaxAuthTries > 0 && authFailures >= config.MaxAuthTries {
819 // If we have hit the max attempts, don't bother sending the
820 // final SSH_MSG_USERAUTH_FAILURE message, since there are
821 // no more authentication methods which can be attempted,
822 // and this message may cause the client to re-attempt
823 // authentication while we send the disconnect message.
824 // Continue, and trigger the disconnect at the start of
825 // the loop.
826 //
827 // The SSH specification is somewhat confusing about this,
828 // RFC 4252 Section 5.1 requires each authentication failure
829 // be responded to with a respective SSH_MSG_USERAUTH_FAILURE
830 // message, but Section 4 says the server should disconnect
831 // after some number of attempts, but it isn't explicit which
832 // message should take precedence (i.e. should there be a failure
833 // message than a disconnect message, or if we are going to
834 // disconnect, should we only send that message.)
835 //
836 // Either way, OpenSSH disconnects immediately after the last
837 // failed authentication attempt, and given they are typically
838 // considered the golden implementation it seems reasonable
839 // to match that behavior.
840 continue
841 }
842 }
843
844 if authConfig.PasswordCallback != nil {
845 failureMsg.Methods = append(failureMsg.Methods, "password")
846 }
847 if authConfig.PublicKeyCallback != nil {
848 failureMsg.Methods = append(failureMsg.Methods, "publickey")
849 }
850 if authConfig.KeyboardInteractiveCallback != nil {
851 failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
852 }
853 if authConfig.GSSAPIWithMICConfig != nil && authConfig.GSSAPIWithMICConfig.Server != nil &&
854 authConfig.GSSAPIWithMICConfig.AllowLogin != nil {
855 failureMsg.Methods = append(failureMsg.Methods, "gssapi-with-mic")
856 }
857
858 if len(failureMsg.Methods) == 0 {
859 return nil, errors.New("ssh: no authentication methods available")
860 }
861
862 if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil {
863 return nil, err
864 }
865 }
866
867 if err := s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil {
868 return nil, err
869 }
870 return perms, nil
871}
872
873// sshClientKeyboardInteractive implements a ClientKeyboardInteractive by
874// asking the client on the other side of a ServerConn.
875type sshClientKeyboardInteractive struct {
876 *connection
877}
878
879func (c *sshClientKeyboardInteractive) Challenge(name, instruction string, questions []string, echos []bool) (answers []string, err error) {
880 if len(questions) != len(echos) {
881 return nil, errors.New("ssh: echos and questions must have equal length")
882 }
883
884 var prompts []byte
885 for i := range questions {
886 prompts = appendString(prompts, questions[i])
887 prompts = appendBool(prompts, echos[i])
888 }
889
890 if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{
891 Name: name,
892 Instruction: instruction,
893 NumPrompts: uint32(len(questions)),
894 Prompts: prompts,
895 })); err != nil {
896 return nil, err
897 }
898
899 packet, err := c.transport.readPacket()
900 if err != nil {
901 return nil, err
902 }
903 if packet[0] != msgUserAuthInfoResponse {
904 return nil, unexpectedMessageError(msgUserAuthInfoResponse, packet[0])
905 }
906 packet = packet[1:]
907
908 n, packet, ok := parseUint32(packet)
909 if !ok || int(n) != len(questions) {
910 return nil, parseError(msgUserAuthInfoResponse)
911 }
912
913 for i := uint32(0); i < n; i++ {
914 ans, rest, ok := parseString(packet)
915 if !ok {
916 return nil, parseError(msgUserAuthInfoResponse)
917 }
918
919 answers = append(answers, string(ans))
920 packet = rest
921 }
922 if len(packet) != 0 {
923 return nil, errors.New("ssh: junk at end of message")
924 }
925
926 return answers, nil
927}