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
48// ServerConfig holds server specific configuration data.
49type ServerConfig struct {
50	// Config contains configuration shared between client and server.
51	Config
52
53	hostKeys []Signer
54
55	// NoClientAuth is true if clients are allowed to connect without
56	// authenticating.
57	NoClientAuth bool
58
59	// MaxAuthTries specifies the maximum number of authentication attempts
60	// permitted per connection. If set to a negative number, the number of
61	// attempts are unlimited. If set to zero, the number of attempts are limited
62	// to 6.
63	MaxAuthTries int
64
65	// PasswordCallback, if non-nil, is called when a user
66	// attempts to authenticate using a password.
67	PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
68
69	// PublicKeyCallback, if non-nil, is called when a client
70	// offers a public key for authentication. It must return a nil error
71	// if the given public key can be used to authenticate the
72	// given user. For example, see CertChecker.Authenticate. A
73	// call to this function does not guarantee that the key
74	// offered is in fact used to authenticate. To record any data
75	// depending on the public key, store it inside a
76	// Permissions.Extensions entry.
77	PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
78
79	// KeyboardInteractiveCallback, if non-nil, is called when
80	// keyboard-interactive authentication is selected (RFC
81	// 4256). The client object's Challenge function should be
82	// used to query the user. The callback may offer multiple
83	// Challenge rounds. To avoid information leaks, the client
84	// should be presented a challenge even if the user is
85	// unknown.
86	KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
87
88	// AuthLogCallback, if non-nil, is called to log all authentication
89	// attempts.
90	AuthLogCallback func(conn ConnMetadata, method string, err error)
91
92	// ServerVersion is the version identification string to announce in
93	// the public handshake.
94	// If empty, a reasonable default is used.
95	// Note that RFC 4253 section 4.2 requires that this string start with
96	// "SSH-2.0-".
97	ServerVersion string
98
99	// BannerCallback, if present, is called and the return string is sent to
100	// the client after key exchange completed but before authentication.
101	BannerCallback func(conn ConnMetadata) string
102}
103
104// AddHostKey adds a private key as a host key. If an existing host
105// key exists with the same algorithm, it is overwritten. Each server
106// config must have at least one host key.
107func (s *ServerConfig) AddHostKey(key Signer) {
108	for i, k := range s.hostKeys {
109		if k.PublicKey().Type() == key.PublicKey().Type() {
110			s.hostKeys[i] = key
111			return
112		}
113	}
114
115	s.hostKeys = append(s.hostKeys, key)
116}
117
118// cachedPubKey contains the results of querying whether a public key is
119// acceptable for a user.
120type cachedPubKey struct {
121	user       string
122	pubKeyData []byte
123	result     error
124	perms      *Permissions
125}
126
127const maxCachedPubKeys = 16
128
129// pubKeyCache caches tests for public keys.  Since SSH clients
130// will query whether a public key is acceptable before attempting to
131// authenticate with it, we end up with duplicate queries for public
132// key validity.  The cache only applies to a single ServerConn.
133type pubKeyCache struct {
134	keys []cachedPubKey
135}
136
137// get returns the result for a given user/algo/key tuple.
138func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) {
139	for _, k := range c.keys {
140		if k.user == user && bytes.Equal(k.pubKeyData, pubKeyData) {
141			return k, true
142		}
143	}
144	return cachedPubKey{}, false
145}
146
147// add adds the given tuple to the cache.
148func (c *pubKeyCache) add(candidate cachedPubKey) {
149	if len(c.keys) < maxCachedPubKeys {
150		c.keys = append(c.keys, candidate)
151	}
152}
153
154// ServerConn is an authenticated SSH connection, as seen from the
155// server
156type ServerConn struct {
157	Conn
158
159	// If the succeeding authentication callback returned a
160	// non-nil Permissions pointer, it is stored here.
161	Permissions *Permissions
162}
163
164// NewServerConn starts a new SSH server with c as the underlying
165// transport.  It starts with a handshake and, if the handshake is
166// unsuccessful, it closes the connection and returns an error.  The
167// Request and NewChannel channels must be serviced, or the connection
168// will hang.
169//
170// The returned error may be of type *ServerAuthError for
171// authentication errors.
172func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
173	fullConf := *config
174	fullConf.SetDefaults()
175	if fullConf.MaxAuthTries == 0 {
176		fullConf.MaxAuthTries = 6
177	}
178
179	s := &connection{
180		sshConn: sshConn{conn: c},
181	}
182	perms, err := s.serverHandshake(&fullConf)
183	if err != nil {
184		c.Close()
185		return nil, nil, nil, err
186	}
187	return &ServerConn{s, perms}, s.mux.incomingChannels, s.mux.incomingRequests, nil
188}
189
190// signAndMarshal signs the data with the appropriate algorithm,
191// and serializes the result in SSH wire format.
192func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) {
193	sig, err := k.Sign(rand, data)
194	if err != nil {
195		return nil, err
196	}
197
198	return Marshal(sig), nil
199}
200
201// handshake performs key exchange and user authentication.
202func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) {
203	if len(config.hostKeys) == 0 {
204		return nil, errors.New("ssh: server has no host keys")
205	}
206
207	if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil && config.KeyboardInteractiveCallback == nil {
208		return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
209	}
210
211	if config.ServerVersion != "" {
212		s.serverVersion = []byte(config.ServerVersion)
213	} else {
214		s.serverVersion = []byte(packageVersion)
215	}
216	var err error
217	s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion)
218	if err != nil {
219		return nil, err
220	}
221
222	tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
223	s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config)
224
225	if err := s.transport.waitSession(); err != nil {
226		return nil, err
227	}
228
229	// We just did the key change, so the session ID is established.
230	s.sessionID = s.transport.getSessionID()
231
232	var packet []byte
233	if packet, err = s.transport.readPacket(); err != nil {
234		return nil, err
235	}
236
237	var serviceRequest serviceRequestMsg
238	if err = Unmarshal(packet, &serviceRequest); err != nil {
239		return nil, err
240	}
241	if serviceRequest.Service != serviceUserAuth {
242		return nil, errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating")
243	}
244	serviceAccept := serviceAcceptMsg{
245		Service: serviceUserAuth,
246	}
247	if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil {
248		return nil, err
249	}
250
251	perms, err := s.serverAuthenticate(config)
252	if err != nil {
253		return nil, err
254	}
255	s.mux = newMux(s.transport)
256	return perms, err
257}
258
259func isAcceptableAlgo(algo string) bool {
260	switch algo {
261	case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519,
262		CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
263		return true
264	}
265	return false
266}
267
268func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
269	if addr == nil {
270		return errors.New("ssh: no address known for client, but source-address match required")
271	}
272
273	tcpAddr, ok := addr.(*net.TCPAddr)
274	if !ok {
275		return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr)
276	}
277
278	for _, sourceAddr := range strings.Split(sourceAddrs, ",") {
279		if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil {
280			if allowedIP.Equal(tcpAddr.IP) {
281				return nil
282			}
283		} else {
284			_, ipNet, err := net.ParseCIDR(sourceAddr)
285			if err != nil {
286				return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err)
287			}
288
289			if ipNet.Contains(tcpAddr.IP) {
290				return nil
291			}
292		}
293	}
294
295	return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
296}
297
298// ServerAuthError represents server authentication errors and is
299// sometimes returned by NewServerConn. It appends any authentication
300// errors that may occur, and is returned if all of the authentication
301// methods provided by the user failed to authenticate.
302type ServerAuthError struct {
303	// Errors contains authentication errors returned by the authentication
304	// callback methods. The first entry is typically ErrNoAuth.
305	Errors []error
306}
307
308func (l ServerAuthError) Error() string {
309	var errs []string
310	for _, err := range l.Errors {
311		errs = append(errs, err.Error())
312	}
313	return "[" + strings.Join(errs, ", ") + "]"
314}
315
316// ErrNoAuth is the error value returned if no
317// authentication method has been passed yet. This happens as a normal
318// part of the authentication loop, since the client first tries
319// 'none' authentication to discover available methods.
320// It is returned in ServerAuthError.Errors from NewServerConn.
321var ErrNoAuth = errors.New("ssh: no auth passed yet")
322
323func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
324	sessionID := s.transport.getSessionID()
325	var cache pubKeyCache
326	var perms *Permissions
327
328	authFailures := 0
329	var authErrs []error
330	var displayedBanner bool
331
332userAuthLoop:
333	for {
334		if authFailures >= config.MaxAuthTries && config.MaxAuthTries > 0 {
335			discMsg := &disconnectMsg{
336				Reason:  2,
337				Message: "too many authentication failures",
338			}
339
340			if err := s.transport.writePacket(Marshal(discMsg)); err != nil {
341				return nil, err
342			}
343
344			return nil, discMsg
345		}
346
347		var userAuthReq userAuthRequestMsg
348		if packet, err := s.transport.readPacket(); err != nil {
349			if err == io.EOF {
350				return nil, &ServerAuthError{Errors: authErrs}
351			}
352			return nil, err
353		} else if err = Unmarshal(packet, &userAuthReq); err != nil {
354			return nil, err
355		}
356
357		if userAuthReq.Service != serviceSSH {
358			return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service)
359		}
360
361		s.user = userAuthReq.User
362
363		if !displayedBanner && config.BannerCallback != nil {
364			displayedBanner = true
365			msg := config.BannerCallback(s)
366			if msg != "" {
367				bannerMsg := &userAuthBannerMsg{
368					Message: msg,
369				}
370				if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
371					return nil, err
372				}
373			}
374		}
375
376		perms = nil
377		authErr := ErrNoAuth
378
379		switch userAuthReq.Method {
380		case "none":
381			if config.NoClientAuth {
382				authErr = nil
383			}
384
385			// allow initial attempt of 'none' without penalty
386			if authFailures == 0 {
387				authFailures--
388			}
389		case "password":
390			if config.PasswordCallback == nil {
391				authErr = errors.New("ssh: password auth not configured")
392				break
393			}
394			payload := userAuthReq.Payload
395			if len(payload) < 1 || payload[0] != 0 {
396				return nil, parseError(msgUserAuthRequest)
397			}
398			payload = payload[1:]
399			password, payload, ok := parseString(payload)
400			if !ok || len(payload) > 0 {
401				return nil, parseError(msgUserAuthRequest)
402			}
403
404			perms, authErr = config.PasswordCallback(s, password)
405		case "keyboard-interactive":
406			if config.KeyboardInteractiveCallback == nil {
407				authErr = errors.New("ssh: keyboard-interactive auth not configured")
408				break
409			}
410
411			prompter := &sshClientKeyboardInteractive{s}
412			perms, authErr = config.KeyboardInteractiveCallback(s, prompter.Challenge)
413		case "publickey":
414			if config.PublicKeyCallback == nil {
415				authErr = errors.New("ssh: publickey auth not configured")
416				break
417			}
418			payload := userAuthReq.Payload
419			if len(payload) < 1 {
420				return nil, parseError(msgUserAuthRequest)
421			}
422			isQuery := payload[0] == 0
423			payload = payload[1:]
424			algoBytes, payload, ok := parseString(payload)
425			if !ok {
426				return nil, parseError(msgUserAuthRequest)
427			}
428			algo := string(algoBytes)
429			if !isAcceptableAlgo(algo) {
430				authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
431				break
432			}
433
434			pubKeyData, payload, ok := parseString(payload)
435			if !ok {
436				return nil, parseError(msgUserAuthRequest)
437			}
438
439			pubKey, err := ParsePublicKey(pubKeyData)
440			if err != nil {
441				return nil, err
442			}
443
444			candidate, ok := cache.get(s.user, pubKeyData)
445			if !ok {
446				candidate.user = s.user
447				candidate.pubKeyData = pubKeyData
448				candidate.perms, candidate.result = config.PublicKeyCallback(s, pubKey)
449				if candidate.result == nil && candidate.perms != nil && candidate.perms.CriticalOptions != nil && candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" {
450					candidate.result = checkSourceAddress(
451						s.RemoteAddr(),
452						candidate.perms.CriticalOptions[sourceAddressCriticalOption])
453				}
454				cache.add(candidate)
455			}
456
457			if isQuery {
458				// The client can query if the given public key
459				// would be okay.
460
461				if len(payload) > 0 {
462					return nil, parseError(msgUserAuthRequest)
463				}
464
465				if candidate.result == nil {
466					okMsg := userAuthPubKeyOkMsg{
467						Algo:   algo,
468						PubKey: pubKeyData,
469					}
470					if err = s.transport.writePacket(Marshal(&okMsg)); err != nil {
471						return nil, err
472					}
473					continue userAuthLoop
474				}
475				authErr = candidate.result
476			} else {
477				sig, payload, ok := parseSignature(payload)
478				if !ok || len(payload) > 0 {
479					return nil, parseError(msgUserAuthRequest)
480				}
481				// Ensure the public key algo and signature algo
482				// are supported.  Compare the private key
483				// algorithm name that corresponds to algo with
484				// sig.Format.  This is usually the same, but
485				// for certs, the names differ.
486				if !isAcceptableAlgo(sig.Format) {
487					authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
488					break
489				}
490				signedData := buildDataSignedForAuth(sessionID, userAuthReq, algoBytes, pubKeyData)
491
492				if err := pubKey.Verify(signedData, sig); err != nil {
493					return nil, err
494				}
495
496				authErr = candidate.result
497				perms = candidate.perms
498			}
499		default:
500			authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method)
501		}
502
503		authErrs = append(authErrs, authErr)
504
505		if config.AuthLogCallback != nil {
506			config.AuthLogCallback(s, userAuthReq.Method, authErr)
507		}
508
509		if authErr == nil {
510			break userAuthLoop
511		}
512
513		authFailures++
514
515		var failureMsg userAuthFailureMsg
516		if config.PasswordCallback != nil {
517			failureMsg.Methods = append(failureMsg.Methods, "password")
518		}
519		if config.PublicKeyCallback != nil {
520			failureMsg.Methods = append(failureMsg.Methods, "publickey")
521		}
522		if config.KeyboardInteractiveCallback != nil {
523			failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
524		}
525
526		if len(failureMsg.Methods) == 0 {
527			return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
528		}
529
530		if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil {
531			return nil, err
532		}
533	}
534
535	if err := s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil {
536		return nil, err
537	}
538	return perms, nil
539}
540
541// sshClientKeyboardInteractive implements a ClientKeyboardInteractive by
542// asking the client on the other side of a ServerConn.
543type sshClientKeyboardInteractive struct {
544	*connection
545}
546
547func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
548	if len(questions) != len(echos) {
549		return nil, errors.New("ssh: echos and questions must have equal length")
550	}
551
552	var prompts []byte
553	for i := range questions {
554		prompts = appendString(prompts, questions[i])
555		prompts = appendBool(prompts, echos[i])
556	}
557
558	if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{
559		Instruction: instruction,
560		NumPrompts:  uint32(len(questions)),
561		Prompts:     prompts,
562	})); err != nil {
563		return nil, err
564	}
565
566	packet, err := c.transport.readPacket()
567	if err != nil {
568		return nil, err
569	}
570	if packet[0] != msgUserAuthInfoResponse {
571		return nil, unexpectedMessageError(msgUserAuthInfoResponse, packet[0])
572	}
573	packet = packet[1:]
574
575	n, packet, ok := parseUint32(packet)
576	if !ok || int(n) != len(questions) {
577		return nil, parseError(msgUserAuthInfoResponse)
578	}
579
580	for i := uint32(0); i < n; i++ {
581		ans, rest, ok := parseString(packet)
582		if !ok {
583			return nil, parseError(msgUserAuthInfoResponse)
584		}
585
586		answers = append(answers, string(ans))
587		packet = rest
588	}
589	if len(packet) != 0 {
590		return nil, errors.New("ssh: junk at end of message")
591	}
592
593	return answers, nil
594}
595