diff options
author | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
---|---|---|
committer | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
commit | bae9f6d2fd5eb5bc80929bd393932b23f14d7c93 (patch) | |
tree | ca9ab12a7d78b1fc27a8f734729081357ce6d252 /vendor/golang.org/x/crypto/ssh/server.go | |
parent | 254c495b6bebab3fb72a243c4bce858d79e6ee99 (diff) | |
download | terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.gz terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.zst terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.zip |
Initial transfer of provider code
Diffstat (limited to 'vendor/golang.org/x/crypto/ssh/server.go')
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/server.go | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go new file mode 100644 index 0000000..77c84d1 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/server.go | |||
@@ -0,0 +1,491 @@ | |||
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 | } | ||