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