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