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.
16 // The Permissions type holds fine-grained permissions that are
17 // specific to a user or a specific authentication method for a
18 // user. Permissions, except for "source-address", must be enforced in
19 // the server application layer, after successful authentication. The
20 // Permissions are passed on in ServerConn so a server implementation
22 type Permissions struct {
23 // Critical options restrict default permissions. Common
24 // restrictions are "source-address" and "force-command". If
25 // the server cannot enforce the restriction, or does not
26 // recognize it, the user should not authenticate.
27 CriticalOptions map[string]string
29 // Extensions are extra functionality that the server may
30 // offer on authenticated connections. Common extensions are
31 // "permit-agent-forwarding", "permit-X11-forwarding". Lack of
32 // support for an extension does not preclude authenticating a
34 Extensions map[string]string
37 // ServerConfig holds server specific configuration data.
38 type ServerConfig struct {
39 // Config contains configuration shared between client and server.
44 // NoClientAuth is true if clients are allowed to connect without
48 // PasswordCallback, if non-nil, is called when a user
49 // attempts to authenticate using a password.
50 PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
52 // PublicKeyCallback, if non-nil, is called when a client attempts public
53 // key authentication. It must return true if the given public key is
54 // valid for the given user. For example, see CertChecker.Authenticate.
55 PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
57 // KeyboardInteractiveCallback, if non-nil, is called when
58 // keyboard-interactive authentication is selected (RFC
59 // 4256). The client object's Challenge function should be
60 // used to query the user. The callback may offer multiple
61 // Challenge rounds. To avoid information leaks, the client
62 // should be presented a challenge even if the user is
64 KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
66 // AuthLogCallback, if non-nil, is called to log all authentication
68 AuthLogCallback func(conn ConnMetadata, method string, err error)
70 // ServerVersion is the version identification string to announce in
71 // the public handshake.
72 // If empty, a reasonable default is used.
73 // Note that RFC 4253 section 4.2 requires that this string start with
78 // AddHostKey adds a private key as a host key. If an existing host
79 // key exists with the same algorithm, it is overwritten. Each server
80 // config must have at least one host key.
81 func (s *ServerConfig) AddHostKey(key Signer) {
82 for i, k := range s.hostKeys {
83 if k.PublicKey().Type() == key.PublicKey().Type() {
89 s.hostKeys = append(s.hostKeys, key)
92 // cachedPubKey contains the results of querying whether a public key is
93 // acceptable for a user.
94 type cachedPubKey struct {
101 const maxCachedPubKeys = 16
103 // pubKeyCache caches tests for public keys. Since SSH clients
104 // will query whether a public key is acceptable before attempting to
105 // authenticate with it, we end up with duplicate queries for public
106 // key validity. The cache only applies to a single ServerConn.
107 type pubKeyCache struct {
111 // get returns the result for a given user/algo/key tuple.
112 func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) {
113 for _, k := range c.keys {
114 if k.user == user && bytes.Equal(k.pubKeyData, pubKeyData) {
118 return cachedPubKey{}, false
121 // add adds the given tuple to the cache.
122 func (c *pubKeyCache) add(candidate cachedPubKey) {
123 if len(c.keys) < maxCachedPubKeys {
124 c.keys = append(c.keys, candidate)
128 // ServerConn is an authenticated SSH connection, as seen from the
130 type ServerConn struct {
133 // If the succeeding authentication callback returned a
134 // non-nil Permissions pointer, it is stored here.
135 Permissions *Permissions
138 // NewServerConn starts a new SSH server with c as the underlying
139 // transport. It starts with a handshake and, if the handshake is
140 // unsuccessful, it closes the connection and returns an error. The
141 // Request and NewChannel channels must be serviced, or the connection
143 func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
145 fullConf.SetDefaults()
147 sshConn: sshConn{conn: c},
149 perms, err := s.serverHandshake(&fullConf)
152 return nil, nil, nil, err
154 return &ServerConn{s, perms}, s.mux.incomingChannels, s.mux.incomingRequests, nil
157 // signAndMarshal signs the data with the appropriate algorithm,
158 // and serializes the result in SSH wire format.
159 func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) {
160 sig, err := k.Sign(rand, data)
165 return Marshal(sig), nil
168 // handshake performs key exchange and user authentication.
169 func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) {
170 if len(config.hostKeys) == 0 {
171 return nil, errors.New("ssh: server has no host keys")
174 if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil && config.KeyboardInteractiveCallback == nil {
175 return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
178 if config.ServerVersion != "" {
179 s.serverVersion = []byte(config.ServerVersion)
181 s.serverVersion = []byte(packageVersion)
184 s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion)
189 tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
190 s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config)
192 if err := s.transport.waitSession(); err != nil {
196 // We just did the key change, so the session ID is established.
197 s.sessionID = s.transport.getSessionID()
200 if packet, err = s.transport.readPacket(); err != nil {
204 var serviceRequest serviceRequestMsg
205 if err = Unmarshal(packet, &serviceRequest); err != nil {
208 if serviceRequest.Service != serviceUserAuth {
209 return nil, errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating")
211 serviceAccept := serviceAcceptMsg{
212 Service: serviceUserAuth,
214 if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil {
218 perms, err := s.serverAuthenticate(config)
222 s.mux = newMux(s.transport)
226 func isAcceptableAlgo(algo string) bool {
228 case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519,
229 CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
235 func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
237 return errors.New("ssh: no address known for client, but source-address match required")
240 tcpAddr, ok := addr.(*net.TCPAddr)
242 return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr)
245 for _, sourceAddr := range strings.Split(sourceAddrs, ",") {
246 if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil {
247 if allowedIP.Equal(tcpAddr.IP) {
251 _, ipNet, err := net.ParseCIDR(sourceAddr)
253 return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err)
256 if ipNet.Contains(tcpAddr.IP) {
262 return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
265 func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
266 sessionID := s.transport.getSessionID()
267 var cache pubKeyCache
268 var perms *Permissions
272 var userAuthReq userAuthRequestMsg
273 if packet, err := s.transport.readPacket(); err != nil {
275 } else if err = Unmarshal(packet, &userAuthReq); err != nil {
279 if userAuthReq.Service != serviceSSH {
280 return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service)
283 s.user = userAuthReq.User
285 authErr := errors.New("no auth passed yet")
287 switch userAuthReq.Method {
289 if config.NoClientAuth {
293 if config.PasswordCallback == nil {
294 authErr = errors.New("ssh: password auth not configured")
297 payload := userAuthReq.Payload
298 if len(payload) < 1 || payload[0] != 0 {
299 return nil, parseError(msgUserAuthRequest)
301 payload = payload[1:]
302 password, payload, ok := parseString(payload)
303 if !ok || len(payload) > 0 {
304 return nil, parseError(msgUserAuthRequest)
307 perms, authErr = config.PasswordCallback(s, password)
308 case "keyboard-interactive":
309 if config.KeyboardInteractiveCallback == nil {
310 authErr = errors.New("ssh: keyboard-interactive auth not configubred")
314 prompter := &sshClientKeyboardInteractive{s}
315 perms, authErr = config.KeyboardInteractiveCallback(s, prompter.Challenge)
317 if config.PublicKeyCallback == nil {
318 authErr = errors.New("ssh: publickey auth not configured")
321 payload := userAuthReq.Payload
322 if len(payload) < 1 {
323 return nil, parseError(msgUserAuthRequest)
325 isQuery := payload[0] == 0
326 payload = payload[1:]
327 algoBytes, payload, ok := parseString(payload)
329 return nil, parseError(msgUserAuthRequest)
331 algo := string(algoBytes)
332 if !isAcceptableAlgo(algo) {
333 authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
337 pubKeyData, payload, ok := parseString(payload)
339 return nil, parseError(msgUserAuthRequest)
342 pubKey, err := ParsePublicKey(pubKeyData)
347 candidate, ok := cache.get(s.user, pubKeyData)
349 candidate.user = s.user
350 candidate.pubKeyData = pubKeyData
351 candidate.perms, candidate.result = config.PublicKeyCallback(s, pubKey)
352 if candidate.result == nil && candidate.perms != nil && candidate.perms.CriticalOptions != nil && candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" {
353 candidate.result = checkSourceAddress(
355 candidate.perms.CriticalOptions[sourceAddressCriticalOption])
361 // The client can query if the given public key
363 if len(payload) > 0 {
364 return nil, parseError(msgUserAuthRequest)
367 if candidate.result == nil {
368 okMsg := userAuthPubKeyOkMsg{
372 if err = s.transport.writePacket(Marshal(&okMsg)); err != nil {
375 continue userAuthLoop
377 authErr = candidate.result
379 sig, payload, ok := parseSignature(payload)
380 if !ok || len(payload) > 0 {
381 return nil, parseError(msgUserAuthRequest)
383 // Ensure the public key algo and signature algo
384 // are supported. Compare the private key
385 // algorithm name that corresponds to algo with
386 // sig.Format. This is usually the same, but
387 // for certs, the names differ.
388 if !isAcceptableAlgo(sig.Format) {
391 signedData := buildDataSignedForAuth(sessionID, userAuthReq, algoBytes, pubKeyData)
393 if err := pubKey.Verify(signedData, sig); err != nil {
397 authErr = candidate.result
398 perms = candidate.perms
401 authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method)
404 if config.AuthLogCallback != nil {
405 config.AuthLogCallback(s, userAuthReq.Method, authErr)
412 var failureMsg userAuthFailureMsg
413 if config.PasswordCallback != nil {
414 failureMsg.Methods = append(failureMsg.Methods, "password")
416 if config.PublicKeyCallback != nil {
417 failureMsg.Methods = append(failureMsg.Methods, "publickey")
419 if config.KeyboardInteractiveCallback != nil {
420 failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
423 if len(failureMsg.Methods) == 0 {
424 return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
427 if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil {
432 if err := s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil {
438 // sshClientKeyboardInteractive implements a ClientKeyboardInteractive by
439 // asking the client on the other side of a ServerConn.
440 type sshClientKeyboardInteractive struct {
444 func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
445 if len(questions) != len(echos) {
446 return nil, errors.New("ssh: echos and questions must have equal length")
450 for i := range questions {
451 prompts = appendString(prompts, questions[i])
452 prompts = appendBool(prompts, echos[i])
455 if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{
456 Instruction: instruction,
457 NumPrompts: uint32(len(questions)),
463 packet, err := c.transport.readPacket()
467 if packet[0] != msgUserAuthInfoResponse {
468 return nil, unexpectedMessageError(msgUserAuthInfoResponse, packet[0])
472 n, packet, ok := parseUint32(packet)
473 if !ok || int(n) != len(questions) {
474 return nil, parseError(msgUserAuthInfoResponse)
477 for i := uint32(0); i < n; i++ {
478 ans, rest, ok := parseString(packet)
480 return nil, parseError(msgUserAuthInfoResponse)
483 answers = append(answers, string(ans))
486 if len(packet) != 0 {
487 return nil, errors.New("ssh: junk at end of message")