]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | // Copyright 2012 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 | "sort" | |
14 | "time" | |
15 | ) | |
16 | ||
17 | // These constants from [PROTOCOL.certkeys] represent the algorithm names | |
18 | // for certificate types supported by this package. | |
19 | const ( | |
20 | CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com" | |
21 | CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com" | |
22 | CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com" | |
23 | CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com" | |
24 | CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com" | |
25 | CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com" | |
26 | ) | |
27 | ||
28 | // Certificate types distinguish between host and user | |
29 | // certificates. The values can be set in the CertType field of | |
30 | // Certificate. | |
31 | const ( | |
32 | UserCert = 1 | |
33 | HostCert = 2 | |
34 | ) | |
35 | ||
36 | // Signature represents a cryptographic signature. | |
37 | type Signature struct { | |
38 | Format string | |
39 | Blob []byte | |
40 | } | |
41 | ||
42 | // CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that | |
43 | // a certificate does not expire. | |
44 | const CertTimeInfinity = 1<<64 - 1 | |
45 | ||
46 | // An Certificate represents an OpenSSH certificate as defined in | |
47 | // [PROTOCOL.certkeys]?rev=1.8. | |
48 | type Certificate struct { | |
49 | Nonce []byte | |
50 | Key PublicKey | |
51 | Serial uint64 | |
52 | CertType uint32 | |
53 | KeyId string | |
54 | ValidPrincipals []string | |
55 | ValidAfter uint64 | |
56 | ValidBefore uint64 | |
57 | Permissions | |
58 | Reserved []byte | |
59 | SignatureKey PublicKey | |
60 | Signature *Signature | |
61 | } | |
62 | ||
63 | // genericCertData holds the key-independent part of the certificate data. | |
64 | // Overall, certificates contain an nonce, public key fields and | |
65 | // key-independent fields. | |
66 | type genericCertData struct { | |
67 | Serial uint64 | |
68 | CertType uint32 | |
69 | KeyId string | |
70 | ValidPrincipals []byte | |
71 | ValidAfter uint64 | |
72 | ValidBefore uint64 | |
73 | CriticalOptions []byte | |
74 | Extensions []byte | |
75 | Reserved []byte | |
76 | SignatureKey []byte | |
77 | Signature []byte | |
78 | } | |
79 | ||
80 | func marshalStringList(namelist []string) []byte { | |
81 | var to []byte | |
82 | for _, name := range namelist { | |
83 | s := struct{ N string }{name} | |
84 | to = append(to, Marshal(&s)...) | |
85 | } | |
86 | return to | |
87 | } | |
88 | ||
89 | type optionsTuple struct { | |
90 | Key string | |
91 | Value []byte | |
92 | } | |
93 | ||
94 | type optionsTupleValue struct { | |
95 | Value string | |
96 | } | |
97 | ||
98 | // serialize a map of critical options or extensions | |
99 | // issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, | |
100 | // we need two length prefixes for a non-empty string value | |
101 | func marshalTuples(tups map[string]string) []byte { | |
102 | keys := make([]string, 0, len(tups)) | |
103 | for key := range tups { | |
104 | keys = append(keys, key) | |
105 | } | |
106 | sort.Strings(keys) | |
107 | ||
108 | var ret []byte | |
109 | for _, key := range keys { | |
110 | s := optionsTuple{Key: key} | |
111 | if value := tups[key]; len(value) > 0 { | |
112 | s.Value = Marshal(&optionsTupleValue{value}) | |
113 | } | |
114 | ret = append(ret, Marshal(&s)...) | |
115 | } | |
116 | return ret | |
117 | } | |
118 | ||
119 | // issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, | |
120 | // we need two length prefixes for a non-empty option value | |
121 | func parseTuples(in []byte) (map[string]string, error) { | |
122 | tups := map[string]string{} | |
123 | var lastKey string | |
124 | var haveLastKey bool | |
125 | ||
126 | for len(in) > 0 { | |
127 | var key, val, extra []byte | |
128 | var ok bool | |
129 | ||
130 | if key, in, ok = parseString(in); !ok { | |
131 | return nil, errShortRead | |
132 | } | |
133 | keyStr := string(key) | |
134 | // according to [PROTOCOL.certkeys], the names must be in | |
135 | // lexical order. | |
136 | if haveLastKey && keyStr <= lastKey { | |
137 | return nil, fmt.Errorf("ssh: certificate options are not in lexical order") | |
138 | } | |
139 | lastKey, haveLastKey = keyStr, true | |
140 | // the next field is a data field, which if non-empty has a string embedded | |
141 | if val, in, ok = parseString(in); !ok { | |
142 | return nil, errShortRead | |
143 | } | |
144 | if len(val) > 0 { | |
145 | val, extra, ok = parseString(val) | |
146 | if !ok { | |
147 | return nil, errShortRead | |
148 | } | |
149 | if len(extra) > 0 { | |
150 | return nil, fmt.Errorf("ssh: unexpected trailing data after certificate option value") | |
151 | } | |
152 | tups[keyStr] = string(val) | |
153 | } else { | |
154 | tups[keyStr] = "" | |
155 | } | |
156 | } | |
157 | return tups, nil | |
158 | } | |
159 | ||
160 | func parseCert(in []byte, privAlgo string) (*Certificate, error) { | |
161 | nonce, rest, ok := parseString(in) | |
162 | if !ok { | |
163 | return nil, errShortRead | |
164 | } | |
165 | ||
166 | key, rest, err := parsePubKey(rest, privAlgo) | |
167 | if err != nil { | |
168 | return nil, err | |
169 | } | |
170 | ||
171 | var g genericCertData | |
172 | if err := Unmarshal(rest, &g); err != nil { | |
173 | return nil, err | |
174 | } | |
175 | ||
176 | c := &Certificate{ | |
177 | Nonce: nonce, | |
178 | Key: key, | |
179 | Serial: g.Serial, | |
180 | CertType: g.CertType, | |
181 | KeyId: g.KeyId, | |
182 | ValidAfter: g.ValidAfter, | |
183 | ValidBefore: g.ValidBefore, | |
184 | } | |
185 | ||
186 | for principals := g.ValidPrincipals; len(principals) > 0; { | |
187 | principal, rest, ok := parseString(principals) | |
188 | if !ok { | |
189 | return nil, errShortRead | |
190 | } | |
191 | c.ValidPrincipals = append(c.ValidPrincipals, string(principal)) | |
192 | principals = rest | |
193 | } | |
194 | ||
195 | c.CriticalOptions, err = parseTuples(g.CriticalOptions) | |
196 | if err != nil { | |
197 | return nil, err | |
198 | } | |
199 | c.Extensions, err = parseTuples(g.Extensions) | |
200 | if err != nil { | |
201 | return nil, err | |
202 | } | |
203 | c.Reserved = g.Reserved | |
204 | k, err := ParsePublicKey(g.SignatureKey) | |
205 | if err != nil { | |
206 | return nil, err | |
207 | } | |
208 | ||
209 | c.SignatureKey = k | |
210 | c.Signature, rest, ok = parseSignatureBody(g.Signature) | |
211 | if !ok || len(rest) > 0 { | |
212 | return nil, errors.New("ssh: signature parse error") | |
213 | } | |
214 | ||
215 | return c, nil | |
216 | } | |
217 | ||
218 | type openSSHCertSigner struct { | |
219 | pub *Certificate | |
220 | signer Signer | |
221 | } | |
222 | ||
223 | // NewCertSigner returns a Signer that signs with the given Certificate, whose | |
224 | // private key is held by signer. It returns an error if the public key in cert | |
225 | // doesn't match the key used by signer. | |
226 | func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) { | |
227 | if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { | |
228 | return nil, errors.New("ssh: signer and cert have different public key") | |
229 | } | |
230 | ||
231 | return &openSSHCertSigner{cert, signer}, nil | |
232 | } | |
233 | ||
234 | func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { | |
235 | return s.signer.Sign(rand, data) | |
236 | } | |
237 | ||
238 | func (s *openSSHCertSigner) PublicKey() PublicKey { | |
239 | return s.pub | |
240 | } | |
241 | ||
242 | const sourceAddressCriticalOption = "source-address" | |
243 | ||
244 | // CertChecker does the work of verifying a certificate. Its methods | |
245 | // can be plugged into ClientConfig.HostKeyCallback and | |
246 | // ServerConfig.PublicKeyCallback. For the CertChecker to work, | |
247 | // minimally, the IsAuthority callback should be set. | |
248 | type CertChecker struct { | |
249 | // SupportedCriticalOptions lists the CriticalOptions that the | |
250 | // server application layer understands. These are only used | |
251 | // for user certificates. | |
252 | SupportedCriticalOptions []string | |
253 | ||
254 | // IsAuthority should return true if the key is recognized as | |
255 | // an authority. This allows for certificates to be signed by other | |
256 | // certificates. | |
257 | IsAuthority func(auth PublicKey) bool | |
258 | ||
259 | // Clock is used for verifying time stamps. If nil, time.Now | |
260 | // is used. | |
261 | Clock func() time.Time | |
262 | ||
263 | // UserKeyFallback is called when CertChecker.Authenticate encounters a | |
264 | // public key that is not a certificate. It must implement validation | |
265 | // of user keys or else, if nil, all such keys are rejected. | |
266 | UserKeyFallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) | |
267 | ||
268 | // HostKeyFallback is called when CertChecker.CheckHostKey encounters a | |
269 | // public key that is not a certificate. It must implement host key | |
270 | // validation or else, if nil, all such keys are rejected. | |
271 | HostKeyFallback func(addr string, remote net.Addr, key PublicKey) error | |
272 | ||
273 | // IsRevoked is called for each certificate so that revocation checking | |
274 | // can be implemented. It should return true if the given certificate | |
275 | // is revoked and false otherwise. If nil, no certificates are | |
276 | // considered to have been revoked. | |
277 | IsRevoked func(cert *Certificate) bool | |
278 | } | |
279 | ||
280 | // CheckHostKey checks a host key certificate. This method can be | |
281 | // plugged into ClientConfig.HostKeyCallback. | |
282 | func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) error { | |
283 | cert, ok := key.(*Certificate) | |
284 | if !ok { | |
285 | if c.HostKeyFallback != nil { | |
286 | return c.HostKeyFallback(addr, remote, key) | |
287 | } | |
288 | return errors.New("ssh: non-certificate host key") | |
289 | } | |
290 | if cert.CertType != HostCert { | |
291 | return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType) | |
292 | } | |
293 | ||
294 | return c.CheckCert(addr, cert) | |
295 | } | |
296 | ||
297 | // Authenticate checks a user certificate. Authenticate can be used as | |
298 | // a value for ServerConfig.PublicKeyCallback. | |
299 | func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error) { | |
300 | cert, ok := pubKey.(*Certificate) | |
301 | if !ok { | |
302 | if c.UserKeyFallback != nil { | |
303 | return c.UserKeyFallback(conn, pubKey) | |
304 | } | |
305 | return nil, errors.New("ssh: normal key pairs not accepted") | |
306 | } | |
307 | ||
308 | if cert.CertType != UserCert { | |
309 | return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType) | |
310 | } | |
311 | ||
312 | if err := c.CheckCert(conn.User(), cert); err != nil { | |
313 | return nil, err | |
314 | } | |
315 | ||
316 | return &cert.Permissions, nil | |
317 | } | |
318 | ||
319 | // CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and | |
320 | // the signature of the certificate. | |
321 | func (c *CertChecker) CheckCert(principal string, cert *Certificate) error { | |
322 | if c.IsRevoked != nil && c.IsRevoked(cert) { | |
323 | return fmt.Errorf("ssh: certicate serial %d revoked", cert.Serial) | |
324 | } | |
325 | ||
326 | for opt, _ := range cert.CriticalOptions { | |
327 | // sourceAddressCriticalOption will be enforced by | |
328 | // serverAuthenticate | |
329 | if opt == sourceAddressCriticalOption { | |
330 | continue | |
331 | } | |
332 | ||
333 | found := false | |
334 | for _, supp := range c.SupportedCriticalOptions { | |
335 | if supp == opt { | |
336 | found = true | |
337 | break | |
338 | } | |
339 | } | |
340 | if !found { | |
341 | return fmt.Errorf("ssh: unsupported critical option %q in certificate", opt) | |
342 | } | |
343 | } | |
344 | ||
345 | if len(cert.ValidPrincipals) > 0 { | |
346 | // By default, certs are valid for all users/hosts. | |
347 | found := false | |
348 | for _, p := range cert.ValidPrincipals { | |
349 | if p == principal { | |
350 | found = true | |
351 | break | |
352 | } | |
353 | } | |
354 | if !found { | |
355 | return fmt.Errorf("ssh: principal %q not in the set of valid principals for given certificate: %q", principal, cert.ValidPrincipals) | |
356 | } | |
357 | } | |
358 | ||
359 | if !c.IsAuthority(cert.SignatureKey) { | |
360 | return fmt.Errorf("ssh: certificate signed by unrecognized authority") | |
361 | } | |
362 | ||
363 | clock := c.Clock | |
364 | if clock == nil { | |
365 | clock = time.Now | |
366 | } | |
367 | ||
368 | unixNow := clock().Unix() | |
369 | if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) { | |
370 | return fmt.Errorf("ssh: cert is not yet valid") | |
371 | } | |
372 | if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) { | |
373 | return fmt.Errorf("ssh: cert has expired") | |
374 | } | |
375 | if err := cert.SignatureKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil { | |
376 | return fmt.Errorf("ssh: certificate signature does not verify") | |
377 | } | |
378 | ||
379 | return nil | |
380 | } | |
381 | ||
382 | // SignCert sets c.SignatureKey to the authority's public key and stores a | |
383 | // Signature, by authority, in the certificate. | |
384 | func (c *Certificate) SignCert(rand io.Reader, authority Signer) error { | |
385 | c.Nonce = make([]byte, 32) | |
386 | if _, err := io.ReadFull(rand, c.Nonce); err != nil { | |
387 | return err | |
388 | } | |
389 | c.SignatureKey = authority.PublicKey() | |
390 | ||
391 | sig, err := authority.Sign(rand, c.bytesForSigning()) | |
392 | if err != nil { | |
393 | return err | |
394 | } | |
395 | c.Signature = sig | |
396 | return nil | |
397 | } | |
398 | ||
399 | var certAlgoNames = map[string]string{ | |
400 | KeyAlgoRSA: CertAlgoRSAv01, | |
401 | KeyAlgoDSA: CertAlgoDSAv01, | |
402 | KeyAlgoECDSA256: CertAlgoECDSA256v01, | |
403 | KeyAlgoECDSA384: CertAlgoECDSA384v01, | |
404 | KeyAlgoECDSA521: CertAlgoECDSA521v01, | |
405 | KeyAlgoED25519: CertAlgoED25519v01, | |
406 | } | |
407 | ||
408 | // certToPrivAlgo returns the underlying algorithm for a certificate algorithm. | |
409 | // Panics if a non-certificate algorithm is passed. | |
410 | func certToPrivAlgo(algo string) string { | |
411 | for privAlgo, pubAlgo := range certAlgoNames { | |
412 | if pubAlgo == algo { | |
413 | return privAlgo | |
414 | } | |
415 | } | |
416 | panic("unknown cert algorithm") | |
417 | } | |
418 | ||
419 | func (cert *Certificate) bytesForSigning() []byte { | |
420 | c2 := *cert | |
421 | c2.Signature = nil | |
422 | out := c2.Marshal() | |
423 | // Drop trailing signature length. | |
424 | return out[:len(out)-4] | |
425 | } | |
426 | ||
427 | // Marshal serializes c into OpenSSH's wire format. It is part of the | |
428 | // PublicKey interface. | |
429 | func (c *Certificate) Marshal() []byte { | |
430 | generic := genericCertData{ | |
431 | Serial: c.Serial, | |
432 | CertType: c.CertType, | |
433 | KeyId: c.KeyId, | |
434 | ValidPrincipals: marshalStringList(c.ValidPrincipals), | |
435 | ValidAfter: uint64(c.ValidAfter), | |
436 | ValidBefore: uint64(c.ValidBefore), | |
437 | CriticalOptions: marshalTuples(c.CriticalOptions), | |
438 | Extensions: marshalTuples(c.Extensions), | |
439 | Reserved: c.Reserved, | |
440 | SignatureKey: c.SignatureKey.Marshal(), | |
441 | } | |
442 | if c.Signature != nil { | |
443 | generic.Signature = Marshal(c.Signature) | |
444 | } | |
445 | genericBytes := Marshal(&generic) | |
446 | keyBytes := c.Key.Marshal() | |
447 | _, keyBytes, _ = parseString(keyBytes) | |
448 | prefix := Marshal(&struct { | |
449 | Name string | |
450 | Nonce []byte | |
451 | Key []byte `ssh:"rest"` | |
452 | }{c.Type(), c.Nonce, keyBytes}) | |
453 | ||
454 | result := make([]byte, 0, len(prefix)+len(genericBytes)) | |
455 | result = append(result, prefix...) | |
456 | result = append(result, genericBytes...) | |
457 | return result | |
458 | } | |
459 | ||
460 | // Type returns the key name. It is part of the PublicKey interface. | |
461 | func (c *Certificate) Type() string { | |
462 | algo, ok := certAlgoNames[c.Key.Type()] | |
463 | if !ok { | |
464 | panic("unknown cert key type " + c.Key.Type()) | |
465 | } | |
466 | return algo | |
467 | } | |
468 | ||
469 | // Verify verifies a signature against the certificate's public | |
470 | // key. It is part of the PublicKey interface. | |
471 | func (c *Certificate) Verify(data []byte, sig *Signature) error { | |
472 | return c.Key.Verify(data, sig) | |
473 | } | |
474 | ||
475 | func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) { | |
476 | format, in, ok := parseString(in) | |
477 | if !ok { | |
478 | return | |
479 | } | |
480 | ||
481 | out = &Signature{ | |
482 | Format: string(format), | |
483 | } | |
484 | ||
485 | if out.Blob, in, ok = parseString(in); !ok { | |
486 | return | |
487 | } | |
488 | ||
489 | return out, in, ok | |
490 | } | |
491 | ||
492 | func parseSignature(in []byte) (out *Signature, rest []byte, ok bool) { | |
493 | sigBytes, rest, ok := parseString(in) | |
494 | if !ok { | |
495 | return | |
496 | } | |
497 | ||
498 | out, trailing, ok := parseSignatureBody(sigBytes) | |
499 | if !ok || len(trailing) > 0 { | |
500 | return nil, nil, false | |
501 | } | |
502 | return | |
503 | } |