github.com/ssube/gitlab-ci-multi-runner@v1.2.1-0.20160607142738-b8d1285632e6/Godeps/_workspace/src/golang.org/x/crypto/ssh/server.go (about)

     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  	"net"
    13  )
    14  
    15  // The Permissions type holds fine-grained permissions that are
    16  // specific to a user or a specific authentication method for a
    17  // user. Permissions, except for "source-address", must be enforced in
    18  // the server application layer, after successful authentication. The
    19  // Permissions are passed on in ServerConn so a server implementation
    20  // can honor them.
    21  type Permissions struct {
    22  	// Critical options restrict default permissions. Common
    23  	// restrictions are "source-address" and "force-command". If
    24  	// the server cannot enforce the restriction, or does not
    25  	// recognize it, the user should not authenticate.
    26  	CriticalOptions map[string]string
    27  
    28  	// Extensions are extra functionality that the server may
    29  	// offer on authenticated connections. Common extensions are
    30  	// "permit-agent-forwarding", "permit-X11-forwarding". Lack of
    31  	// support for an extension does not preclude authenticating a
    32  	// user.
    33  	Extensions map[string]string
    34  }
    35  
    36  // ServerConfig holds server specific configuration data.
    37  type ServerConfig struct {
    38  	// Config contains configuration shared between client and server.
    39  	Config
    40  
    41  	hostKeys []Signer
    42  
    43  	// NoClientAuth is true if clients are allowed to connect without
    44  	// authenticating.
    45  	NoClientAuth bool
    46  
    47  	// PasswordCallback, if non-nil, is called when a user
    48  	// attempts to authenticate using a password.
    49  	PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
    50  
    51  	// PublicKeyCallback, if non-nil, is called when a client attempts public
    52  	// key authentication. It must return true if the given public key is
    53  	// valid for the given user. For example, see CertChecker.Authenticate.
    54  	PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
    55  
    56  	// KeyboardInteractiveCallback, if non-nil, is called when
    57  	// keyboard-interactive authentication is selected (RFC
    58  	// 4256). The client object's Challenge function should be
    59  	// used to query the user. The callback may offer multiple
    60  	// Challenge rounds. To avoid information leaks, the client
    61  	// should be presented a challenge even if the user is
    62  	// unknown.
    63  	KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
    64  
    65  	// AuthLogCallback, if non-nil, is called to log all authentication
    66  	// attempts.
    67  	AuthLogCallback func(conn ConnMetadata, method string, err error)
    68  
    69  	// ServerVersion is the version identification string to announce in
    70  	// the public handshake.
    71  	// If empty, a reasonable default is used.
    72  	// Note that RFC 4253 section 4.2 requires that this string start with
    73  	// "SSH-2.0-".
    74  	ServerVersion string
    75  }
    76  
    77  // AddHostKey adds a private key as a host key. If an existing host
    78  // key exists with the same algorithm, it is overwritten. Each server
    79  // config must have at least one host key.
    80  func (s *ServerConfig) AddHostKey(key Signer) {
    81  	for i, k := range s.hostKeys {
    82  		if k.PublicKey().Type() == key.PublicKey().Type() {
    83  			s.hostKeys[i] = key
    84  			return
    85  		}
    86  	}
    87  
    88  	s.hostKeys = append(s.hostKeys, key)
    89  }
    90  
    91  // cachedPubKey contains the results of querying whether a public key is
    92  // acceptable for a user.
    93  type cachedPubKey struct {
    94  	user       string
    95  	pubKeyData []byte
    96  	result     error
    97  	perms      *Permissions
    98  }
    99  
   100  const maxCachedPubKeys = 16
   101  
   102  // pubKeyCache caches tests for public keys.  Since SSH clients
   103  // will query whether a public key is acceptable before attempting to
   104  // authenticate with it, we end up with duplicate queries for public
   105  // key validity.  The cache only applies to a single ServerConn.
   106  type pubKeyCache struct {
   107  	keys []cachedPubKey
   108  }
   109  
   110  // get returns the result for a given user/algo/key tuple.
   111  func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) {
   112  	for _, k := range c.keys {
   113  		if k.user == user && bytes.Equal(k.pubKeyData, pubKeyData) {
   114  			return k, true
   115  		}
   116  	}
   117  	return cachedPubKey{}, false
   118  }
   119  
   120  // add adds the given tuple to the cache.
   121  func (c *pubKeyCache) add(candidate cachedPubKey) {
   122  	if len(c.keys) < maxCachedPubKeys {
   123  		c.keys = append(c.keys, candidate)
   124  	}
   125  }
   126  
   127  // ServerConn is an authenticated SSH connection, as seen from the
   128  // server
   129  type ServerConn struct {
   130  	Conn
   131  
   132  	// If the succeeding authentication callback returned a
   133  	// non-nil Permissions pointer, it is stored here.
   134  	Permissions *Permissions
   135  }
   136  
   137  // NewServerConn starts a new SSH server with c as the underlying
   138  // transport.  It starts with a handshake and, if the handshake is
   139  // unsuccessful, it closes the connection and returns an error.  The
   140  // Request and NewChannel channels must be serviced, or the connection
   141  // will hang.
   142  func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
   143  	fullConf := *config
   144  	fullConf.SetDefaults()
   145  	s := &connection{
   146  		sshConn: sshConn{conn: c},
   147  	}
   148  	perms, err := s.serverHandshake(&fullConf)
   149  	if err != nil {
   150  		c.Close()
   151  		return nil, nil, nil, err
   152  	}
   153  	return &ServerConn{s, perms}, s.mux.incomingChannels, s.mux.incomingRequests, nil
   154  }
   155  
   156  // signAndMarshal signs the data with the appropriate algorithm,
   157  // and serializes the result in SSH wire format.
   158  func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) {
   159  	sig, err := k.Sign(rand, data)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	return Marshal(sig), nil
   165  }
   166  
   167  // handshake performs key exchange and user authentication.
   168  func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) {
   169  	if len(config.hostKeys) == 0 {
   170  		return nil, errors.New("ssh: server has no host keys")
   171  	}
   172  
   173  	if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil && config.KeyboardInteractiveCallback == nil {
   174  		return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
   175  	}
   176  
   177  	if config.ServerVersion != "" {
   178  		s.serverVersion = []byte(config.ServerVersion)
   179  	} else {
   180  		s.serverVersion = []byte(packageVersion)
   181  	}
   182  	var err error
   183  	s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  
   188  	tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
   189  	s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config)
   190  
   191  	if err := s.transport.requestKeyChange(); err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	if packet, err := s.transport.readPacket(); err != nil {
   196  		return nil, err
   197  	} else if packet[0] != msgNewKeys {
   198  		return nil, unexpectedMessageError(msgNewKeys, packet[0])
   199  	}
   200  
   201  	// We just did the key change, so the session ID is established.
   202  	s.sessionID = s.transport.getSessionID()
   203  
   204  	var packet []byte
   205  	if packet, err = s.transport.readPacket(); err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	var serviceRequest serviceRequestMsg
   210  	if err = Unmarshal(packet, &serviceRequest); err != nil {
   211  		return nil, err
   212  	}
   213  	if serviceRequest.Service != serviceUserAuth {
   214  		return nil, errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating")
   215  	}
   216  	serviceAccept := serviceAcceptMsg{
   217  		Service: serviceUserAuth,
   218  	}
   219  	if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	perms, err := s.serverAuthenticate(config)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  	s.mux = newMux(s.transport)
   228  	return perms, err
   229  }
   230  
   231  func isAcceptableAlgo(algo string) bool {
   232  	switch algo {
   233  	case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
   234  		CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
   235  		return true
   236  	}
   237  	return false
   238  }
   239  
   240  func checkSourceAddress(addr net.Addr, sourceAddr string) error {
   241  	if addr == nil {
   242  		return errors.New("ssh: no address known for client, but source-address match required")
   243  	}
   244  
   245  	tcpAddr, ok := addr.(*net.TCPAddr)
   246  	if !ok {
   247  		return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr)
   248  	}
   249  
   250  	if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil {
   251  		if bytes.Equal(allowedIP, tcpAddr.IP) {
   252  			return nil
   253  		}
   254  	} else {
   255  		_, ipNet, err := net.ParseCIDR(sourceAddr)
   256  		if err != nil {
   257  			return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err)
   258  		}
   259  
   260  		if ipNet.Contains(tcpAddr.IP) {
   261  			return nil
   262  		}
   263  	}
   264  
   265  	return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
   266  }
   267  
   268  func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
   269  	var err error
   270  	var cache pubKeyCache
   271  	var perms *Permissions
   272  
   273  userAuthLoop:
   274  	for {
   275  		var userAuthReq userAuthRequestMsg
   276  		if packet, err := s.transport.readPacket(); err != nil {
   277  			return nil, err
   278  		} else if err = Unmarshal(packet, &userAuthReq); err != nil {
   279  			return nil, err
   280  		}
   281  
   282  		if userAuthReq.Service != serviceSSH {
   283  			return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service)
   284  		}
   285  
   286  		s.user = userAuthReq.User
   287  		perms = nil
   288  		authErr := errors.New("no auth passed yet")
   289  
   290  		switch userAuthReq.Method {
   291  		case "none":
   292  			if config.NoClientAuth {
   293  				s.user = ""
   294  				authErr = nil
   295  			}
   296  		case "password":
   297  			if config.PasswordCallback == nil {
   298  				authErr = errors.New("ssh: password auth not configured")
   299  				break
   300  			}
   301  			payload := userAuthReq.Payload
   302  			if len(payload) < 1 || payload[0] != 0 {
   303  				return nil, parseError(msgUserAuthRequest)
   304  			}
   305  			payload = payload[1:]
   306  			password, payload, ok := parseString(payload)
   307  			if !ok || len(payload) > 0 {
   308  				return nil, parseError(msgUserAuthRequest)
   309  			}
   310  
   311  			perms, authErr = config.PasswordCallback(s, password)
   312  		case "keyboard-interactive":
   313  			if config.KeyboardInteractiveCallback == nil {
   314  				authErr = errors.New("ssh: keyboard-interactive auth not configubred")
   315  				break
   316  			}
   317  
   318  			prompter := &sshClientKeyboardInteractive{s}
   319  			perms, authErr = config.KeyboardInteractiveCallback(s, prompter.Challenge)
   320  		case "publickey":
   321  			if config.PublicKeyCallback == nil {
   322  				authErr = errors.New("ssh: publickey auth not configured")
   323  				break
   324  			}
   325  			payload := userAuthReq.Payload
   326  			if len(payload) < 1 {
   327  				return nil, parseError(msgUserAuthRequest)
   328  			}
   329  			isQuery := payload[0] == 0
   330  			payload = payload[1:]
   331  			algoBytes, payload, ok := parseString(payload)
   332  			if !ok {
   333  				return nil, parseError(msgUserAuthRequest)
   334  			}
   335  			algo := string(algoBytes)
   336  			if !isAcceptableAlgo(algo) {
   337  				authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
   338  				break
   339  			}
   340  
   341  			pubKeyData, payload, ok := parseString(payload)
   342  			if !ok {
   343  				return nil, parseError(msgUserAuthRequest)
   344  			}
   345  
   346  			pubKey, err := ParsePublicKey(pubKeyData)
   347  			if err != nil {
   348  				return nil, err
   349  			}
   350  
   351  			candidate, ok := cache.get(s.user, pubKeyData)
   352  			if !ok {
   353  				candidate.user = s.user
   354  				candidate.pubKeyData = pubKeyData
   355  				candidate.perms, candidate.result = config.PublicKeyCallback(s, pubKey)
   356  				if candidate.result == nil && candidate.perms != nil && candidate.perms.CriticalOptions != nil && candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" {
   357  					candidate.result = checkSourceAddress(
   358  						s.RemoteAddr(),
   359  						candidate.perms.CriticalOptions[sourceAddressCriticalOption])
   360  				}
   361  				cache.add(candidate)
   362  			}
   363  
   364  			if isQuery {
   365  				// The client can query if the given public key
   366  				// would be okay.
   367  				if len(payload) > 0 {
   368  					return nil, parseError(msgUserAuthRequest)
   369  				}
   370  
   371  				if candidate.result == nil {
   372  					okMsg := userAuthPubKeyOkMsg{
   373  						Algo:   algo,
   374  						PubKey: pubKeyData,
   375  					}
   376  					if err = s.transport.writePacket(Marshal(&okMsg)); err != nil {
   377  						return nil, err
   378  					}
   379  					continue userAuthLoop
   380  				}
   381  				authErr = candidate.result
   382  			} else {
   383  				sig, payload, ok := parseSignature(payload)
   384  				if !ok || len(payload) > 0 {
   385  					return nil, parseError(msgUserAuthRequest)
   386  				}
   387  				// Ensure the public key algo and signature algo
   388  				// are supported.  Compare the private key
   389  				// algorithm name that corresponds to algo with
   390  				// sig.Format.  This is usually the same, but
   391  				// for certs, the names differ.
   392  				if !isAcceptableAlgo(sig.Format) {
   393  					break
   394  				}
   395  				signedData := buildDataSignedForAuth(s.transport.getSessionID(), userAuthReq, algoBytes, pubKeyData)
   396  
   397  				if err := pubKey.Verify(signedData, sig); err != nil {
   398  					return nil, err
   399  				}
   400  
   401  				authErr = candidate.result
   402  				perms = candidate.perms
   403  			}
   404  		default:
   405  			authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method)
   406  		}
   407  
   408  		if config.AuthLogCallback != nil {
   409  			config.AuthLogCallback(s, userAuthReq.Method, authErr)
   410  		}
   411  
   412  		if authErr == nil {
   413  			break userAuthLoop
   414  		}
   415  
   416  		var failureMsg userAuthFailureMsg
   417  		if config.PasswordCallback != nil {
   418  			failureMsg.Methods = append(failureMsg.Methods, "password")
   419  		}
   420  		if config.PublicKeyCallback != nil {
   421  			failureMsg.Methods = append(failureMsg.Methods, "publickey")
   422  		}
   423  		if config.KeyboardInteractiveCallback != nil {
   424  			failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
   425  		}
   426  
   427  		if len(failureMsg.Methods) == 0 {
   428  			return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
   429  		}
   430  
   431  		if err = s.transport.writePacket(Marshal(&failureMsg)); err != nil {
   432  			return nil, err
   433  		}
   434  	}
   435  
   436  	if err = s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil {
   437  		return nil, err
   438  	}
   439  	return perms, nil
   440  }
   441  
   442  // sshClientKeyboardInteractive implements a ClientKeyboardInteractive by
   443  // asking the client on the other side of a ServerConn.
   444  type sshClientKeyboardInteractive struct {
   445  	*connection
   446  }
   447  
   448  func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
   449  	if len(questions) != len(echos) {
   450  		return nil, errors.New("ssh: echos and questions must have equal length")
   451  	}
   452  
   453  	var prompts []byte
   454  	for i := range questions {
   455  		prompts = appendString(prompts, questions[i])
   456  		prompts = appendBool(prompts, echos[i])
   457  	}
   458  
   459  	if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{
   460  		Instruction: instruction,
   461  		NumPrompts:  uint32(len(questions)),
   462  		Prompts:     prompts,
   463  	})); err != nil {
   464  		return nil, err
   465  	}
   466  
   467  	packet, err := c.transport.readPacket()
   468  	if err != nil {
   469  		return nil, err
   470  	}
   471  	if packet[0] != msgUserAuthInfoResponse {
   472  		return nil, unexpectedMessageError(msgUserAuthInfoResponse, packet[0])
   473  	}
   474  	packet = packet[1:]
   475  
   476  	n, packet, ok := parseUint32(packet)
   477  	if !ok || int(n) != len(questions) {
   478  		return nil, parseError(msgUserAuthInfoResponse)
   479  	}
   480  
   481  	for i := uint32(0); i < n; i++ {
   482  		ans, rest, ok := parseString(packet)
   483  		if !ok {
   484  			return nil, parseError(msgUserAuthInfoResponse)
   485  		}
   486  
   487  		answers = append(answers, string(ans))
   488  		packet = rest
   489  	}
   490  	if len(packet) != 0 {
   491  		return nil, errors.New("ssh: junk at end of message")
   492  	}
   493  
   494  	return answers, nil
   495  }