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