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