These are the cosmetic cleanups from the bits of code that I rereviewed. 1) stringLength now takes a int; the length of the string. Too many callers were allocating with stringLength([]byte(s)) and stringLength only needs to call len(). 2) agent.go now has sendAndReceive to remove logic that was duplicated. 3) We now reject negative DH values 4) We now reject empty packets rather than crashing. R=dave, jonathan.mark.pittman CC=golang-dev https://golang.org/cl/6061052
305 lines
7.1 KiB
Go
305 lines
7.1 KiB
Go
// Copyright 2012 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package ssh
|
|
|
|
// References
|
|
// [PROTOCOL.certkeys]: http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys
|
|
|
|
import (
|
|
"crypto/dsa"
|
|
"crypto/rsa"
|
|
"time"
|
|
)
|
|
|
|
// String constants in [PROTOCOL.certkeys] for certificate algorithm names.
|
|
const (
|
|
hostAlgoRSACertV01 = "ssh-rsa-cert-v01@openssh.com"
|
|
hostAlgoDSACertV01 = "ssh-dss-cert-v01@openssh.com"
|
|
hostAlgoECDSA256CertV01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
|
hostAlgoECDSA384CertV01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
|
|
hostAlgoECDSA521CertV01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
|
|
)
|
|
|
|
// Certificate types are used to specify whether a certificate is for identification
|
|
// of a user or a host. Current identities are defined in [PROTOCOL.certkeys].
|
|
const (
|
|
UserCert = 1
|
|
HostCert = 2
|
|
)
|
|
|
|
type signature struct {
|
|
Format string
|
|
Blob []byte
|
|
}
|
|
|
|
type tuple struct {
|
|
Name string
|
|
Data string
|
|
}
|
|
|
|
// An OpenSSHCertV01 represents an OpenSSH certificate as defined in
|
|
// [PROTOCOL.certkeys] rev 1.8. Supported formats include
|
|
// ssh-rsa-cert-v01@openssh.com and ssh-dss-cert-v01@openssh.com.
|
|
type OpenSSHCertV01 struct {
|
|
Nonce []byte
|
|
Key interface{} // rsa or dsa *PublicKey
|
|
Serial uint64
|
|
Type uint32
|
|
KeyId string
|
|
ValidPrincipals []string
|
|
ValidAfter, ValidBefore time.Time
|
|
CriticalOptions []tuple
|
|
Extensions []tuple
|
|
Reserved []byte
|
|
SignatureKey interface{} // rsa, dsa, or ecdsa *PublicKey
|
|
Signature *signature
|
|
}
|
|
|
|
func parseOpenSSHCertV01(in []byte, algo string) (out *OpenSSHCertV01, rest []byte, ok bool) {
|
|
cert := new(OpenSSHCertV01)
|
|
|
|
if cert.Nonce, in, ok = parseString(in); !ok {
|
|
return
|
|
}
|
|
|
|
switch algo {
|
|
case hostAlgoRSACertV01:
|
|
var rsaPubKey *rsa.PublicKey
|
|
if rsaPubKey, in, ok = parseRSA(in); !ok {
|
|
return
|
|
}
|
|
cert.Key = rsaPubKey
|
|
case hostAlgoDSACertV01:
|
|
var dsaPubKey *dsa.PublicKey
|
|
if dsaPubKey, in, ok = parseDSA(in); !ok {
|
|
return
|
|
}
|
|
cert.Key = dsaPubKey
|
|
default:
|
|
return
|
|
}
|
|
|
|
if cert.Serial, in, ok = parseUint64(in); !ok {
|
|
return
|
|
}
|
|
|
|
if cert.Type, in, ok = parseUint32(in); !ok || cert.Type != UserCert && cert.Type != HostCert {
|
|
return
|
|
}
|
|
|
|
keyId, in, ok := parseString(in)
|
|
if !ok {
|
|
return
|
|
}
|
|
cert.KeyId = string(keyId)
|
|
|
|
if cert.ValidPrincipals, in, ok = parseLengthPrefixedNameList(in); !ok {
|
|
return
|
|
}
|
|
|
|
va, in, ok := parseUint64(in)
|
|
if !ok {
|
|
return
|
|
}
|
|
cert.ValidAfter = time.Unix(int64(va), 0)
|
|
|
|
vb, in, ok := parseUint64(in)
|
|
if !ok {
|
|
return
|
|
}
|
|
cert.ValidBefore = time.Unix(int64(vb), 0)
|
|
|
|
if cert.CriticalOptions, in, ok = parseTupleList(in); !ok {
|
|
return
|
|
}
|
|
|
|
if cert.Extensions, in, ok = parseTupleList(in); !ok {
|
|
return
|
|
}
|
|
|
|
if cert.Reserved, in, ok = parseString(in); !ok {
|
|
return
|
|
}
|
|
|
|
sigKey, in, ok := parseString(in)
|
|
if !ok {
|
|
return
|
|
}
|
|
if cert.SignatureKey, _, ok = parsePubKey(sigKey); !ok {
|
|
return
|
|
}
|
|
|
|
if cert.Signature, in, ok = parseSignature(in); !ok {
|
|
return
|
|
}
|
|
|
|
ok = true
|
|
return cert, in, ok
|
|
}
|
|
|
|
func marshalOpenSSHCertV01(cert *OpenSSHCertV01) []byte {
|
|
var pubKey []byte
|
|
switch cert.Key.(type) {
|
|
case *rsa.PublicKey:
|
|
k := cert.Key.(*rsa.PublicKey)
|
|
pubKey = marshalPubRSA(k)
|
|
case *dsa.PublicKey:
|
|
k := cert.Key.(*dsa.PublicKey)
|
|
pubKey = marshalPubDSA(k)
|
|
default:
|
|
panic("ssh: unknown public key type in cert")
|
|
}
|
|
|
|
sigKey := serializePublickey(cert.SignatureKey)
|
|
|
|
length := stringLength(len(cert.Nonce))
|
|
length += len(pubKey)
|
|
length += 8 // Length of Serial
|
|
length += 4 // Length of Type
|
|
length += stringLength(len(cert.KeyId))
|
|
length += lengthPrefixedNameListLength(cert.ValidPrincipals)
|
|
length += 8 // Length of ValidAfter
|
|
length += 8 // Length of ValidBefore
|
|
length += tupleListLength(cert.CriticalOptions)
|
|
length += tupleListLength(cert.Extensions)
|
|
length += stringLength(len(cert.Reserved))
|
|
length += stringLength(len(sigKey))
|
|
length += signatureLength(cert.Signature)
|
|
|
|
ret := make([]byte, length)
|
|
r := marshalString(ret, cert.Nonce)
|
|
copy(r, pubKey)
|
|
r = r[len(pubKey):]
|
|
r = marshalUint64(r, cert.Serial)
|
|
r = marshalUint32(r, cert.Type)
|
|
r = marshalString(r, []byte(cert.KeyId))
|
|
r = marshalLengthPrefixedNameList(r, cert.ValidPrincipals)
|
|
r = marshalUint64(r, uint64(cert.ValidAfter.Unix()))
|
|
r = marshalUint64(r, uint64(cert.ValidBefore.Unix()))
|
|
r = marshalTupleList(r, cert.CriticalOptions)
|
|
r = marshalTupleList(r, cert.Extensions)
|
|
r = marshalString(r, cert.Reserved)
|
|
r = marshalString(r, sigKey)
|
|
r = marshalSignature(r, cert.Signature)
|
|
if len(r) > 0 {
|
|
panic("internal error")
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func lengthPrefixedNameListLength(namelist []string) int {
|
|
length := 4 // length prefix for list
|
|
for _, name := range namelist {
|
|
length += 4 // length prefix for name
|
|
length += len(name)
|
|
}
|
|
return length
|
|
}
|
|
|
|
func marshalLengthPrefixedNameList(to []byte, namelist []string) []byte {
|
|
length := uint32(lengthPrefixedNameListLength(namelist) - 4)
|
|
to = marshalUint32(to, length)
|
|
for _, name := range namelist {
|
|
to = marshalString(to, []byte(name))
|
|
}
|
|
return to
|
|
}
|
|
|
|
func parseLengthPrefixedNameList(in []byte) (out []string, rest []byte, ok bool) {
|
|
list, rest, ok := parseString(in)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
for len(list) > 0 {
|
|
var next []byte
|
|
if next, list, ok = parseString(list); !ok {
|
|
return nil, nil, false
|
|
}
|
|
out = append(out, string(next))
|
|
}
|
|
ok = true
|
|
return
|
|
}
|
|
|
|
func tupleListLength(tupleList []tuple) int {
|
|
length := 4 // length prefix for list
|
|
for _, t := range tupleList {
|
|
length += 4 // length prefix for t.Name
|
|
length += len(t.Name)
|
|
length += 4 // length prefix for t.Data
|
|
length += len(t.Data)
|
|
}
|
|
return length
|
|
}
|
|
|
|
func marshalTupleList(to []byte, tuplelist []tuple) []byte {
|
|
length := uint32(tupleListLength(tuplelist) - 4)
|
|
to = marshalUint32(to, length)
|
|
for _, t := range tuplelist {
|
|
to = marshalString(to, []byte(t.Name))
|
|
to = marshalString(to, []byte(t.Data))
|
|
}
|
|
return to
|
|
}
|
|
|
|
func parseTupleList(in []byte) (out []tuple, rest []byte, ok bool) {
|
|
list, rest, ok := parseString(in)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
for len(list) > 0 {
|
|
var name, data []byte
|
|
var ok bool
|
|
name, list, ok = parseString(list)
|
|
if !ok {
|
|
return nil, nil, false
|
|
}
|
|
data, list, ok = parseString(list)
|
|
if !ok {
|
|
return nil, nil, false
|
|
}
|
|
out = append(out, tuple{string(name), string(data)})
|
|
}
|
|
ok = true
|
|
return
|
|
}
|
|
|
|
func signatureLength(sig *signature) int {
|
|
length := 4 // length prefix for signature
|
|
length += stringLength(len(sig.Format))
|
|
length += stringLength(len(sig.Blob))
|
|
return length
|
|
}
|
|
|
|
func marshalSignature(to []byte, sig *signature) []byte {
|
|
length := uint32(signatureLength(sig) - 4)
|
|
to = marshalUint32(to, length)
|
|
to = marshalString(to, []byte(sig.Format))
|
|
to = marshalString(to, sig.Blob)
|
|
return to
|
|
}
|
|
|
|
func parseSignature(in []byte) (out *signature, rest []byte, ok bool) {
|
|
var sigBytes, format []byte
|
|
sig := new(signature)
|
|
|
|
if sigBytes, rest, ok = parseString(in); !ok {
|
|
return
|
|
}
|
|
|
|
if format, sigBytes, ok = parseString(sigBytes); !ok {
|
|
return
|
|
}
|
|
sig.Format = string(format)
|
|
|
|
if sig.Blob, sigBytes, ok = parseString(sigBytes); !ok {
|
|
return
|
|
}
|
|
|
|
return sig, rest, ok
|
|
}
|