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