update vendor

This commit is contained in:
deepzz0
2018-01-13 18:23:03 +08:00
parent 6524b45751
commit 06a12bc6f9
36 changed files with 784 additions and 226 deletions

8
glide.lock generated
View File

@@ -1,5 +1,5 @@
hash: c733fa4abeda21b59b001578b37a168bd33038d337b61198cc5fd94be8bfdf77
updated: 2018-01-06T23:16:19.339917+08:00
updated: 2018-01-13T18:22:28.620808+08:00
imports:
- name: github.com/boj/redistore
version: 4562487a4bee9a7c272b72bfaeda4917d0a47ab9
@@ -61,7 +61,7 @@ imports:
- name: github.com/shurcooL/sanitized_anchor_name
version: 86672fcb3f950f35f2e675df2240550f2a50762f
- name: golang.org/x/crypto
version: 0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8
version: 13931e22f9e72ea58bb73048bc752b48c6d4d4ac
subpackages:
- acme
- acme/autocert
@@ -70,7 +70,7 @@ imports:
subpackages:
- context
- name: golang.org/x/sys
version: dd9ec17814d5b1de388ebe438ec013d57d8b3779
version: 810d7000345868fc619eb81f46307107118f4ae1
subpackages:
- unix
- name: gopkg.in/go-playground/validator.v8
@@ -83,7 +83,7 @@ imports:
- internal/sasl
- internal/scram
- name: gopkg.in/yaml.v2
version: 0e4404da71227dcc02fb1deee803d93e86d08f72
version: d670f9405373e636a5a2765eea47fac0c9bc91a4
- name: qiniupkg.com/x
version: 946c4a16076d6d98aeb78619e2bd4012357f7228
subpackages:

View File

@@ -24,7 +24,9 @@ import (
"fmt"
"io"
mathrand "math/rand"
"net"
"net/http"
"path"
"strconv"
"strings"
"sync"
@@ -80,8 +82,9 @@ func defaultHostPolicy(context.Context, string) error {
}
// Manager is a stateful certificate manager built on top of acme.Client.
// It obtains and refreshes certificates automatically,
// as well as providing them to a TLS server via tls.Config.
// It obtains and refreshes certificates automatically using "tls-sni-01",
// "tls-sni-02" and "http-01" challenge types, as well as providing them
// to a TLS server via tls.Config.
//
// You must specify a cache implementation, such as DirCache,
// to reuse obtained certificates across program restarts.
@@ -150,15 +153,26 @@ type Manager struct {
stateMu sync.Mutex
state map[string]*certState // keyed by domain name
// tokenCert is keyed by token domain name, which matches server name
// of ClientHello. Keys always have ".acme.invalid" suffix.
tokenCertMu sync.RWMutex
tokenCert map[string]*tls.Certificate
// renewal tracks the set of domains currently running renewal timers.
// It is keyed by domain name.
renewalMu sync.Mutex
renewal map[string]*domainRenewal
// tokensMu guards the rest of the fields: tryHTTP01, certTokens and httpTokens.
tokensMu sync.RWMutex
// tryHTTP01 indicates whether the Manager should try "http-01" challenge type
// during the authorization flow.
tryHTTP01 bool
// httpTokens contains response body values for http-01 challenges
// and is keyed by the URL path at which a challenge response is expected
// to be provisioned.
// The entries are stored for the duration of the authorization flow.
httpTokens map[string][]byte
// certTokens contains temporary certificates for tls-sni challenges
// and is keyed by token domain name, which matches server name of ClientHello.
// Keys always have ".acme.invalid" suffix.
// The entries are stored for the duration of the authorization flow.
certTokens map[string]*tls.Certificate
}
// GetCertificate implements the tls.Config.GetCertificate hook.
@@ -185,14 +199,16 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
return nil, errors.New("acme/autocert: server name contains invalid character")
}
// In the worst-case scenario, the timeout needs to account for caching, host policy,
// domain ownership verification and certificate issuance.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
// check whether this is a token cert requested for TLS-SNI challenge
if strings.HasSuffix(name, ".acme.invalid") {
m.tokenCertMu.RLock()
defer m.tokenCertMu.RUnlock()
if cert := m.tokenCert[name]; cert != nil {
m.tokensMu.RLock()
defer m.tokensMu.RUnlock()
if cert := m.certTokens[name]; cert != nil {
return cert, nil
}
if cert, err := m.cacheGet(ctx, name); err == nil {
@@ -224,6 +240,68 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
return cert, nil
}
// HTTPHandler configures the Manager to provision ACME "http-01" challenge responses.
// It returns an http.Handler that responds to the challenges and must be
// running on port 80. If it receives a request that is not an ACME challenge,
// it delegates the request to the optional fallback handler.
//
// If fallback is nil, the returned handler redirects all GET and HEAD requests
// to the default TLS port 443 with 302 Found status code, preserving the original
// request path and query. It responds with 400 Bad Request to all other HTTP methods.
// The fallback is not protected by the optional HostPolicy.
//
// Because the fallback handler is run with unencrypted port 80 requests,
// the fallback should not serve TLS-only requests.
//
// If HTTPHandler is never called, the Manager will only use TLS SNI
// challenges for domain verification.
func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler {
m.tokensMu.Lock()
defer m.tokensMu.Unlock()
m.tryHTTP01 = true
if fallback == nil {
fallback = http.HandlerFunc(handleHTTPRedirect)
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.HasPrefix(r.URL.Path, "/.well-known/acme-challenge/") {
fallback.ServeHTTP(w, r)
return
}
// A reasonable context timeout for cache and host policy only,
// because we don't wait for a new certificate issuance here.
ctx, cancel := context.WithTimeout(r.Context(), time.Minute)
defer cancel()
if err := m.hostPolicy()(ctx, r.Host); err != nil {
http.Error(w, err.Error(), http.StatusForbidden)
return
}
data, err := m.httpToken(ctx, r.URL.Path)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
w.Write(data)
})
}
func handleHTTPRedirect(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" && r.Method != "HEAD" {
http.Error(w, "Use HTTPS", http.StatusBadRequest)
return
}
target := "https://" + stripPort(r.Host) + r.URL.RequestURI()
http.Redirect(w, r, target, http.StatusFound)
}
func stripPort(hostport string) string {
host, _, err := net.SplitHostPort(hostport)
if err != nil {
return hostport
}
return net.JoinHostPort(host, "443")
}
// cert returns an existing certificate either from m.state or cache.
// If a certificate is found in cache but not in m.state, the latter will be filled
// with the cached value.
@@ -442,13 +520,14 @@ func (m *Manager) certState(domain string) (*certState, error) {
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
// The key argument is the certificate private key.
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) {
if err := m.verify(ctx, domain); err != nil {
return nil, nil, err
}
client, err := m.acmeClient(ctx)
if err != nil {
return nil, nil, err
}
if err := m.verify(ctx, client, domain); err != nil {
return nil, nil, err
}
csr, err := certRequest(key, domain)
if err != nil {
return nil, nil, err
@@ -464,98 +543,171 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain
return der, leaf, nil
}
// verify starts a new identifier (domain) authorization flow.
// It prepares a challenge response and then blocks until the authorization
// is marked as "completed" by the CA (either succeeded or failed).
//
// verify returns nil iff the verification was successful.
func (m *Manager) verify(ctx context.Context, domain string) error {
client, err := m.acmeClient(ctx)
if err != nil {
return err
// verify runs the identifier (domain) authorization flow
// using each applicable ACME challenge type.
func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
// The list of challenge types we'll try to fulfill
// in this specific order.
challengeTypes := []string{"tls-sni-02", "tls-sni-01"}
m.tokensMu.RLock()
if m.tryHTTP01 {
challengeTypes = append(challengeTypes, "http-01")
}
m.tokensMu.RUnlock()
// start domain authorization and get the challenge
var nextTyp int // challengeType index of the next challenge type to try
for {
// Start domain authorization and get the challenge.
authz, err := client.Authorize(ctx, domain)
if err != nil {
return err
}
// maybe don't need to at all
if authz.Status == acme.StatusValid {
return nil
// No point in accepting challenges if the authorization status
// is in a final state.
switch authz.Status {
case acme.StatusValid:
return nil // already authorized
case acme.StatusInvalid:
return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
}
// pick a challenge: prefer tls-sni-02 over tls-sni-01
// TODO: consider authz.Combinations
// Pick the next preferred challenge.
var chal *acme.Challenge
for _, c := range authz.Challenges {
if c.Type == "tls-sni-02" {
chal = c
break
}
if c.Type == "tls-sni-01" {
chal = c
}
for chal == nil && nextTyp < len(challengeTypes) {
chal = pickChallenge(challengeTypes[nextTyp], authz.Challenges)
nextTyp++
}
if chal == nil {
return errors.New("acme/autocert: no supported challenge type found")
}
// create a token cert for the challenge response
var (
cert tls.Certificate
name string
)
switch chal.Type {
case "tls-sni-01":
cert, name, err = client.TLSSNI01ChallengeCert(chal.Token)
case "tls-sni-02":
cert, name, err = client.TLSSNI02ChallengeCert(chal.Token)
default:
err = fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
return fmt.Errorf("acme/autocert: unable to authorize %q; tried %q", domain, challengeTypes)
}
cleanup, err := m.fulfill(ctx, client, chal)
if err != nil {
return err
continue
}
m.putTokenCert(ctx, name, &cert)
defer func() {
// verification has ended at this point
// don't need token cert anymore
go m.deleteTokenCert(name)
}()
// ready to fulfill the challenge
defer cleanup()
if _, err := client.Accept(ctx, chal); err != nil {
return err
continue
}
// A challenge is fulfilled and accepted: wait for the CA to validate.
if _, err := client.WaitAuthorization(ctx, authz.URI); err == nil {
return nil
}
}
// wait for the CA to validate
_, err = client.WaitAuthorization(ctx, authz.URI)
return err
}
// putTokenCert stores the cert under the named key in both m.tokenCert map
// and m.Cache.
func (m *Manager) putTokenCert(ctx context.Context, name string, cert *tls.Certificate) {
m.tokenCertMu.Lock()
defer m.tokenCertMu.Unlock()
if m.tokenCert == nil {
m.tokenCert = make(map[string]*tls.Certificate)
// fulfill provisions a response to the challenge chal.
// The cleanup is non-nil only if provisioning succeeded.
func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge) (cleanup func(), err error) {
switch chal.Type {
case "tls-sni-01":
cert, name, err := client.TLSSNI01ChallengeCert(chal.Token)
if err != nil {
return nil, err
}
m.tokenCert[name] = cert
m.putCertToken(ctx, name, &cert)
return func() { go m.deleteCertToken(name) }, nil
case "tls-sni-02":
cert, name, err := client.TLSSNI02ChallengeCert(chal.Token)
if err != nil {
return nil, err
}
m.putCertToken(ctx, name, &cert)
return func() { go m.deleteCertToken(name) }, nil
case "http-01":
resp, err := client.HTTP01ChallengeResponse(chal.Token)
if err != nil {
return nil, err
}
p := client.HTTP01ChallengePath(chal.Token)
m.putHTTPToken(ctx, p, resp)
return func() { go m.deleteHTTPToken(p) }, nil
}
return nil, fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
}
func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge {
for _, c := range chal {
if c.Type == typ {
return c
}
}
return nil
}
// putCertToken stores the cert under the named key in both m.certTokens map
// and m.Cache.
func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) {
m.tokensMu.Lock()
defer m.tokensMu.Unlock()
if m.certTokens == nil {
m.certTokens = make(map[string]*tls.Certificate)
}
m.certTokens[name] = cert
m.cachePut(ctx, name, cert)
}
// deleteTokenCert removes the token certificate for the specified domain name
// from both m.tokenCert map and m.Cache.
func (m *Manager) deleteTokenCert(name string) {
m.tokenCertMu.Lock()
defer m.tokenCertMu.Unlock()
delete(m.tokenCert, name)
// deleteCertToken removes the token certificate for the specified domain name
// from both m.certTokens map and m.Cache.
func (m *Manager) deleteCertToken(name string) {
m.tokensMu.Lock()
defer m.tokensMu.Unlock()
delete(m.certTokens, name)
if m.Cache != nil {
m.Cache.Delete(context.Background(), name)
}
}
// httpToken retrieves an existing http-01 token value from an in-memory map
// or the optional cache.
func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, error) {
m.tokensMu.RLock()
defer m.tokensMu.RUnlock()
if v, ok := m.httpTokens[tokenPath]; ok {
return v, nil
}
if m.Cache == nil {
return nil, fmt.Errorf("acme/autocert: no token at %q", tokenPath)
}
return m.Cache.Get(ctx, httpTokenCacheKey(tokenPath))
}
// putHTTPToken stores an http-01 token value using tokenPath as key
// in both in-memory map and the optional Cache.
//
// It ignores any error returned from Cache.Put.
func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) {
m.tokensMu.Lock()
defer m.tokensMu.Unlock()
if m.httpTokens == nil {
m.httpTokens = make(map[string][]byte)
}
b := []byte(val)
m.httpTokens[tokenPath] = b
if m.Cache != nil {
m.Cache.Put(ctx, httpTokenCacheKey(tokenPath), b)
}
}
// deleteHTTPToken removes an http-01 token value from both in-memory map
// and the optional Cache, ignoring any error returned from the latter.
//
// If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout.
func (m *Manager) deleteHTTPToken(tokenPath string) {
m.tokensMu.Lock()
defer m.tokensMu.Unlock()
delete(m.httpTokens, tokenPath)
if m.Cache != nil {
m.Cache.Delete(context.Background(), httpTokenCacheKey(tokenPath))
}
}
// httpTokenCacheKey returns a key at which an http-01 token value may be stored
// in the Manager's optional Cache.
func httpTokenCacheKey(tokenPath string) string {
return "http-01-" + path.Base(tokenPath)
}
// renew starts a cert renewal timer loop, one per domain.
//
// The loop is scheduled in two cases:

View File

@@ -23,6 +23,7 @@ import (
"net/http"
"net/http/httptest"
"reflect"
"strings"
"sync"
"testing"
"time"
@@ -48,6 +49,16 @@ var authzTmpl = template.Must(template.New("authz").Parse(`{
"uri": "{{.}}/challenge/2",
"type": "tls-sni-02",
"token": "token-02"
},
{
"uri": "{{.}}/challenge/dns-01",
"type": "dns-01",
"token": "token-dns-01"
},
{
"uri": "{{.}}/challenge/http-01",
"type": "http-01",
"token": "token-http-01"
}
]
}`))
@@ -419,6 +430,146 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl
}
func TestVerifyHTTP01(t *testing.T) {
var (
http01 http.Handler
authzCount int // num. of created authorizations
didAcceptHTTP01 bool
)
verifyHTTPToken := func() {
r := httptest.NewRequest("GET", "/.well-known/acme-challenge/token-http-01", nil)
w := httptest.NewRecorder()
http01.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Errorf("http token: w.Code = %d; want %d", w.Code, http.StatusOK)
}
if v := string(w.Body.Bytes()); !strings.HasPrefix(v, "token-http-01.") {
t.Errorf("http token value = %q; want 'token-http-01.' prefix", v)
}
}
// ACME CA server stub, only the needed bits.
// TODO: Merge this with startACMEServerStub, making it a configurable CA for testing.
var ca *httptest.Server
ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Replay-Nonce", "nonce")
if r.Method == "HEAD" {
// a nonce request
return
}
switch r.URL.Path {
// Discovery.
case "/":
if err := discoTmpl.Execute(w, ca.URL); err != nil {
t.Errorf("discoTmpl: %v", err)
}
// Client key registration.
case "/new-reg":
w.Write([]byte("{}"))
// New domain authorization.
case "/new-authz":
authzCount++
w.Header().Set("Location", fmt.Sprintf("%s/authz/%d", ca.URL, authzCount))
w.WriteHeader(http.StatusCreated)
if err := authzTmpl.Execute(w, ca.URL); err != nil {
t.Errorf("authzTmpl: %v", err)
}
// Accept tls-sni-02.
case "/challenge/2":
w.Write([]byte("{}"))
// Reject tls-sni-01.
case "/challenge/1":
http.Error(w, "won't accept tls-sni-01", http.StatusBadRequest)
// Should not accept dns-01.
case "/challenge/dns-01":
t.Errorf("dns-01 challenge was accepted")
http.Error(w, "won't accept dns-01", http.StatusBadRequest)
// Accept http-01.
case "/challenge/http-01":
didAcceptHTTP01 = true
verifyHTTPToken()
w.Write([]byte("{}"))
// Authorization statuses.
// Make tls-sni-xxx invalid.
case "/authz/1", "/authz/2":
w.Write([]byte(`{"status": "invalid"}`))
case "/authz/3", "/authz/4":
w.Write([]byte(`{"status": "valid"}`))
default:
http.NotFound(w, r)
t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
}
}))
defer ca.Close()
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
m := &Manager{
Client: &acme.Client{
Key: key,
DirectoryURL: ca.URL,
},
}
http01 = m.HTTPHandler(nil)
if err := m.verify(context.Background(), m.Client, "example.org"); err != nil {
t.Errorf("m.verify: %v", err)
}
// Only tls-sni-01, tls-sni-02 and http-01 must be accepted
// The dns-01 challenge is unsupported.
if authzCount != 3 {
t.Errorf("authzCount = %d; want 3", authzCount)
}
if !didAcceptHTTP01 {
t.Error("did not accept http-01 challenge")
}
}
func TestHTTPHandlerDefaultFallback(t *testing.T) {
tt := []struct {
method, url string
wantCode int
wantLocation string
}{
{"GET", "http://example.org", 302, "https://example.org/"},
{"GET", "http://example.org/foo", 302, "https://example.org/foo"},
{"GET", "http://example.org/foo/bar/", 302, "https://example.org/foo/bar/"},
{"GET", "http://example.org/?a=b", 302, "https://example.org/?a=b"},
{"GET", "http://example.org/foo?a=b", 302, "https://example.org/foo?a=b"},
{"GET", "http://example.org:80/foo?a=b", 302, "https://example.org:443/foo?a=b"},
{"GET", "http://example.org:80/foo%20bar", 302, "https://example.org:443/foo%20bar"},
{"GET", "http://[2602:d1:xxxx::c60a]:1234", 302, "https://[2602:d1:xxxx::c60a]:443/"},
{"GET", "http://[2602:d1:xxxx::c60a]", 302, "https://[2602:d1:xxxx::c60a]/"},
{"GET", "http://[2602:d1:xxxx::c60a]/foo?a=b", 302, "https://[2602:d1:xxxx::c60a]/foo?a=b"},
{"HEAD", "http://example.org", 302, "https://example.org/"},
{"HEAD", "http://example.org/foo", 302, "https://example.org/foo"},
{"HEAD", "http://example.org/foo/bar/", 302, "https://example.org/foo/bar/"},
{"HEAD", "http://example.org/?a=b", 302, "https://example.org/?a=b"},
{"HEAD", "http://example.org/foo?a=b", 302, "https://example.org/foo?a=b"},
{"POST", "http://example.org", 400, ""},
{"PUT", "http://example.org", 400, ""},
{"GET", "http://example.org/.well-known/acme-challenge/x", 404, ""},
}
var m Manager
h := m.HTTPHandler(nil)
for i, test := range tt {
r := httptest.NewRequest(test.method, test.url, nil)
w := httptest.NewRecorder()
h.ServeHTTP(w, r)
if w.Code != test.wantCode {
t.Errorf("%d: w.Code = %d; want %d", i, w.Code, test.wantCode)
t.Errorf("%d: body: %s", i, w.Body.Bytes())
}
if v := w.Header().Get("Location"); v != test.wantLocation {
t.Errorf("%d: Location = %q; want %q", i, v, test.wantLocation)
}
}
}
func TestAccountKeyCache(t *testing.T) {
m := Manager{Cache: newMemCache()}
ctx := context.Background()

View File

@@ -22,11 +22,12 @@ func ExampleNewListener() {
}
func ExampleManager() {
m := autocert.Manager{
m := &autocert.Manager{
Cache: autocert.DirCache("secret-dir"),
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("example.org"),
}
go http.ListenAndServe(":http", m.HTTPHandler(nil))
s := &http.Server{
Addr: ":https",
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},

View File

@@ -7,7 +7,7 @@ package chacha20poly1305
import (
"encoding/binary"
"golang.org/x/crypto/chacha20poly1305/internal/chacha20"
"golang.org/x/crypto/internal/chacha20"
"golang.org/x/crypto/poly1305"
)

View File

@@ -41,6 +41,7 @@ func sshPipe() (Conn, *server, error) {
clientConf := ClientConfig{
User: "user",
HostKeyCallback: InsecureIgnoreHostKey(),
}
serverConf := ServerConfig{
NoClientAuth: true,

View File

@@ -21,12 +21,19 @@ func TestDefaultCiphersExist(t *testing.T) {
}
func TestPacketCiphers(t *testing.T) {
// Still test aes128cbc cipher although it's commented out.
cipherModes[aes128cbcID] = &streamCipherMode{16, aes.BlockSize, 0, nil}
defer delete(cipherModes, aes128cbcID)
defaultMac := "hmac-sha2-256"
defaultCipher := "aes128-ctr"
for cipher := range cipherModes {
t.Run("cipher="+cipher,
func(t *testing.T) { testPacketCipher(t, cipher, defaultMac) })
}
for mac := range macModes {
t.Run("mac="+mac,
func(t *testing.T) { testPacketCipher(t, defaultCipher, mac) })
}
}
func testPacketCipher(t *testing.T, cipher, mac string) {
kr := &kexResult{Hash: crypto.SHA1}
algs := directionAlgorithms{
Cipher: cipher,
@@ -35,34 +42,28 @@ func TestPacketCiphers(t *testing.T) {
}
client, err := newPacketCipher(clientKeys, algs, kr)
if err != nil {
t.Errorf("newPacketCipher(client, %q, %q): %v", cipher, mac, err)
continue
t.Fatalf("newPacketCipher(client, %q, %q): %v", cipher, mac, err)
}
server, err := newPacketCipher(clientKeys, algs, kr)
if err != nil {
t.Errorf("newPacketCipher(client, %q, %q): %v", cipher, mac, err)
continue
t.Fatalf("newPacketCipher(client, %q, %q): %v", cipher, mac, err)
}
want := "bla bla"
input := []byte(want)
buf := &bytes.Buffer{}
if err := client.writePacket(0, buf, rand.Reader, input); err != nil {
t.Errorf("writePacket(%q, %q): %v", cipher, mac, err)
continue
t.Fatalf("writePacket(%q, %q): %v", cipher, mac, err)
}
packet, err := server.readPacket(0, buf)
if err != nil {
t.Errorf("readPacket(%q, %q): %v", cipher, mac, err)
continue
t.Fatalf("readPacket(%q, %q): %v", cipher, mac, err)
}
if string(packet) != want {
t.Errorf("roundtrip(%q, %q): got %q, want %q", cipher, mac, packet, want)
}
}
}
}
func TestCBCOracleCounterMeasure(t *testing.T) {

View File

@@ -5,39 +5,75 @@
package ssh
import (
"net"
"strings"
"testing"
)
func testClientVersion(t *testing.T, config *ClientConfig, expected string) {
clientConn, serverConn := net.Pipe()
defer clientConn.Close()
receivedVersion := make(chan string, 1)
config.HostKeyCallback = InsecureIgnoreHostKey()
go func() {
version, err := readVersion(serverConn)
func TestClientVersion(t *testing.T) {
for _, tt := range []struct {
name string
version string
multiLine string
wantErr bool
}{
{
name: "default version",
version: packageVersion,
},
{
name: "custom version",
version: "SSH-2.0-CustomClientVersionString",
},
{
name: "good multi line version",
version: packageVersion,
multiLine: strings.Repeat("ignored\r\n", 20),
},
{
name: "bad multi line version",
version: packageVersion,
multiLine: "bad multi line version",
wantErr: true,
},
{
name: "long multi line version",
version: packageVersion,
multiLine: strings.Repeat("long multi line version\r\n", 50)[:256],
wantErr: true,
},
} {
t.Run(tt.name, func(t *testing.T) {
c1, c2, err := netPipe()
if err != nil {
receivedVersion <- ""
} else {
receivedVersion <- string(version)
t.Fatalf("netPipe: %v", err)
}
serverConn.Close()
defer c1.Close()
defer c2.Close()
go func() {
if tt.multiLine != "" {
c1.Write([]byte(tt.multiLine))
}
NewClientConn(c1, "", &ClientConfig{
ClientVersion: tt.version,
HostKeyCallback: InsecureIgnoreHostKey(),
})
c1.Close()
}()
NewClientConn(clientConn, "", config)
actual := <-receivedVersion
if actual != expected {
t.Fatalf("got %s; want %s", actual, expected)
conf := &ServerConfig{NoClientAuth: true}
conf.AddHostKey(testSigners["rsa"])
conn, _, _, err := NewServerConn(c2, conf)
if err == nil == tt.wantErr {
t.Fatalf("got err %v; wantErr %t", err, tt.wantErr)
}
if tt.wantErr {
// Don't verify the version on an expected error.
return
}
if got := string(conn.ClientVersion()); got != tt.version {
t.Fatalf("got %q; want %q", got, tt.version)
}
})
}
}
func TestCustomClientVersion(t *testing.T) {
version := "Test-Client-Version-0.0"
testClientVersion(t, &ClientConfig{ClientVersion: version}, version)
}
func TestDefaultClientVersion(t *testing.T) {
testClientVersion(t, &ClientConfig{}, packageVersion)
}
func TestHostKeyCheck(t *testing.T) {

View File

@@ -333,6 +333,7 @@ func TestCiphers(t *testing.T) {
cipherOrder = append(cipherOrder, "aes128-cbc", "3des-cbc")
for _, ciph := range cipherOrder {
t.Run(ciph, func(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conf := clientConfig()
@@ -345,9 +346,9 @@ func TestCiphers(t *testing.T) {
} else {
t.Fatalf("failed for cipher %q", ciph)
}
})
}
}
func TestMACs(t *testing.T) {
var config ssh.Config
config.SetDefaults()

View File

@@ -6,6 +6,7 @@ package ssh
import (
"bufio"
"bytes"
"errors"
"io"
"log"
@@ -342,7 +343,7 @@ func readVersion(r io.Reader) ([]byte, error) {
var ok bool
var buf [1]byte
for len(versionString) < maxVersionStringBytes {
for length := 0; length < maxVersionStringBytes; length++ {
_, err := io.ReadFull(r, buf[:])
if err != nil {
return nil, err
@@ -350,6 +351,13 @@ func readVersion(r io.Reader) ([]byte, error) {
// The RFC says that the version should be terminated with \r\n
// but several SSH servers actually only send a \n.
if buf[0] == '\n' {
if !bytes.HasPrefix(versionString, []byte("SSH-")) {
// RFC 4253 says we need to ignore all version string lines
// except the one containing the SSH version (provided that
// all the lines do not exceed 255 bytes in total).
versionString = versionString[:0]
continue
}
ok = true
break
}

View File

@@ -13,11 +13,13 @@ import (
)
func TestReadVersion(t *testing.T) {
longversion := strings.Repeat("SSH-2.0-bla", 50)[:253]
longVersion := strings.Repeat("SSH-2.0-bla", 50)[:253]
multiLineVersion := strings.Repeat("ignored\r\n", 20) + "SSH-2.0-bla\r\n"
cases := map[string]string{
"SSH-2.0-bla\r\n": "SSH-2.0-bla",
"SSH-2.0-bla\n": "SSH-2.0-bla",
longversion + "\r\n": longversion,
multiLineVersion: "SSH-2.0-bla",
longVersion + "\r\n": longVersion,
}
for in, want := range cases {
@@ -33,9 +35,11 @@ func TestReadVersion(t *testing.T) {
}
func TestReadVersionError(t *testing.T) {
longversion := strings.Repeat("SSH-2.0-bla", 50)[:253]
longVersion := strings.Repeat("SSH-2.0-bla", 50)[:253]
multiLineVersion := strings.Repeat("ignored\r\n", 50) + "SSH-2.0-bla\r\n"
cases := []string{
longversion + "too-long\r\n",
longVersion + "too-long\r\n",
multiLineVersion,
}
for _, in := range cases {
if _, err := readVersion(bytes.NewBufferString(in)); err == nil {
@@ -60,7 +64,7 @@ func TestExchangeVersionsBasic(t *testing.T) {
func TestExchangeVersions(t *testing.T) {
cases := []string{
"not\x000allowed",
"not allowed\n",
"not allowed\x01\r\n",
}
for _, c := range cases {
buf := bytes.NewBufferString("SSH-2.0-bla\r\n")

View File

@@ -25,3 +25,7 @@ func Clearenv() {
func Environ() []string {
return syscall.Environ()
}
func Unsetenv(key string) error {
return syscall.Unsetenv(key)
}

View File

@@ -1,14 +0,0 @@
// Copyright 2014 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.
// +build go1.4
package plan9
import "syscall"
func Unsetenv(key string) error {
// This was added in Go 1.4.
return syscall.Unsetenv(key)
}

View File

@@ -35,7 +35,6 @@ TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
BL runtime·exitsyscall(SB)
RET
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
B syscall·RawSyscall(SB)
@@ -53,5 +52,5 @@ TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
MOVW R0, r2+20(FP)
RET
TEXT ·seek(SB),NOSPLIT,$0-32
TEXT ·seek(SB),NOSPLIT,$0-28
B syscall·seek(SB)

View File

@@ -25,3 +25,7 @@ func Clearenv() {
func Environ() []string {
return syscall.Environ()
}
func Unsetenv(key string) error {
return syscall.Unsetenv(key)
}

View File

@@ -1,14 +0,0 @@
// Copyright 2014 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.
// +build go1.4
package unix
import "syscall"
func Unsetenv(key string) error {
// This was added in Go 1.4.
return syscall.Unsetenv(key)
}

View File

@@ -11,9 +11,19 @@ import "syscall"
// We can't use the gc-syntax .s files for gccgo. On the plus side
// much of the functionality can be written directly in Go.
//extern gccgoRealSyscallNoError
func realSyscallNoError(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r uintptr)
//extern gccgoRealSyscall
func realSyscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r, errno uintptr)
func SyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) {
syscall.Entersyscall()
r := realSyscallNoError(trap, a1, a2, a3, 0, 0, 0, 0, 0, 0)
syscall.Exitsyscall()
return r, 0
}
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
syscall.Entersyscall()
r, errno := realSyscall(trap, a1, a2, a3, 0, 0, 0, 0, 0, 0)
@@ -35,6 +45,11 @@ func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr,
return r, 0, syscall.Errno(errno)
}
func RawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) {
r := realSyscallNoError(trap, a1, a2, a3, 0, 0, 0, 0, 0, 0)
return r, 0
}
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
r, errno := realSyscall(trap, a1, a2, a3, 0, 0, 0, 0, 0, 0)
return r, 0, syscall.Errno(errno)

View File

@@ -31,6 +31,12 @@ gccgoRealSyscall(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintp
return r;
}
uintptr_t
gccgoRealSyscallNoError(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9)
{
return syscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9);
}
// Define the use function in C so that it is not inlined.
extern void use(void *) __asm__ (GOSYM_PREFIX GOPKGPATH ".use") __attribute__((noinline));

View File

@@ -64,6 +64,7 @@ package unix
#include <linux/vm_sockets.h>
#include <linux/random.h>
#include <linux/taskstats.h>
#include <linux/cgroupstats.h>
#include <linux/genetlink.h>
// On mips64, the glibc stat and kernel stat do not agree
@@ -543,7 +544,7 @@ type Termios C.termios_t
type Winsize C.struct_winsize
// Taskstats
// Taskstats and cgroup stats.
type Taskstats C.struct_taskstats
@@ -565,6 +566,18 @@ const (
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = C.TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK
)
type CGroupStats C.struct_cgroupstats
const (
CGROUPSTATS_CMD_UNSPEC = C.__TASKSTATS_CMD_MAX
CGROUPSTATS_CMD_GET = C.CGROUPSTATS_CMD_GET
CGROUPSTATS_CMD_NEW = C.CGROUPSTATS_CMD_NEW
CGROUPSTATS_TYPE_UNSPEC = C.CGROUPSTATS_TYPE_UNSPEC
CGROUPSTATS_TYPE_CGROUP_STATS = C.CGROUPSTATS_TYPE_CGROUP_STATS
CGROUPSTATS_CMD_ATTR_UNSPEC = C.CGROUPSTATS_CMD_ATTR_UNSPEC
CGROUPSTATS_CMD_ATTR_FD = C.CGROUPSTATS_CMD_ATTR_FD
)
// Generic netlink
type Genlmsghdr C.struct_genlmsghdr

View File

@@ -426,6 +426,7 @@ ccflags="$@"
$2 ~ /^(VM|VMADDR)_/ ||
$2 ~ /^IOCTL_VM_SOCKETS_/ ||
$2 ~ /^(TASKSTATS|TS)_/ ||
$2 ~ /^CGROUPSTATS_/ ||
$2 ~ /^GENL_/ ||
$2 ~ /^UTIME_/ ||
$2 ~ /^XATTR_(CREATE|REPLACE)/ ||

View File

@@ -760,6 +760,24 @@ const (
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = 0x4
)
type CGroupStats struct {
Sleeping uint64
Running uint64
Stopped uint64
Uninterruptible uint64
Io_wait uint64
}
const (
CGROUPSTATS_CMD_UNSPEC = 0x3
CGROUPSTATS_CMD_GET = 0x4
CGROUPSTATS_CMD_NEW = 0x5
CGROUPSTATS_TYPE_UNSPEC = 0x0
CGROUPSTATS_TYPE_CGROUP_STATS = 0x1
CGROUPSTATS_CMD_ATTR_UNSPEC = 0x0
CGROUPSTATS_CMD_ATTR_FD = 0x1
)
type Genlmsghdr struct {
Cmd uint8
Version uint8

View File

@@ -778,6 +778,24 @@ const (
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = 0x4
)
type CGroupStats struct {
Sleeping uint64
Running uint64
Stopped uint64
Uninterruptible uint64
Io_wait uint64
}
const (
CGROUPSTATS_CMD_UNSPEC = 0x3
CGROUPSTATS_CMD_GET = 0x4
CGROUPSTATS_CMD_NEW = 0x5
CGROUPSTATS_TYPE_UNSPEC = 0x0
CGROUPSTATS_TYPE_CGROUP_STATS = 0x1
CGROUPSTATS_CMD_ATTR_UNSPEC = 0x0
CGROUPSTATS_CMD_ATTR_FD = 0x1
)
type Genlmsghdr struct {
Cmd uint8
Version uint8

View File

@@ -749,6 +749,24 @@ const (
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = 0x4
)
type CGroupStats struct {
Sleeping uint64
Running uint64
Stopped uint64
Uninterruptible uint64
Io_wait uint64
}
const (
CGROUPSTATS_CMD_UNSPEC = 0x3
CGROUPSTATS_CMD_GET = 0x4
CGROUPSTATS_CMD_NEW = 0x5
CGROUPSTATS_TYPE_UNSPEC = 0x0
CGROUPSTATS_TYPE_CGROUP_STATS = 0x1
CGROUPSTATS_CMD_ATTR_UNSPEC = 0x0
CGROUPSTATS_CMD_ATTR_FD = 0x1
)
type Genlmsghdr struct {
Cmd uint8
Version uint8

View File

@@ -757,6 +757,24 @@ const (
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = 0x4
)
type CGroupStats struct {
Sleeping uint64
Running uint64
Stopped uint64
Uninterruptible uint64
Io_wait uint64
}
const (
CGROUPSTATS_CMD_UNSPEC = 0x3
CGROUPSTATS_CMD_GET = 0x4
CGROUPSTATS_CMD_NEW = 0x5
CGROUPSTATS_TYPE_UNSPEC = 0x0
CGROUPSTATS_TYPE_CGROUP_STATS = 0x1
CGROUPSTATS_CMD_ATTR_UNSPEC = 0x0
CGROUPSTATS_CMD_ATTR_FD = 0x1
)
type Genlmsghdr struct {
Cmd uint8
Version uint8

View File

@@ -754,6 +754,24 @@ const (
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = 0x4
)
type CGroupStats struct {
Sleeping uint64
Running uint64
Stopped uint64
Uninterruptible uint64
Io_wait uint64
}
const (
CGROUPSTATS_CMD_UNSPEC = 0x3
CGROUPSTATS_CMD_GET = 0x4
CGROUPSTATS_CMD_NEW = 0x5
CGROUPSTATS_TYPE_UNSPEC = 0x0
CGROUPSTATS_TYPE_CGROUP_STATS = 0x1
CGROUPSTATS_CMD_ATTR_UNSPEC = 0x0
CGROUPSTATS_CMD_ATTR_FD = 0x1
)
type Genlmsghdr struct {
Cmd uint8
Version uint8

View File

@@ -759,6 +759,24 @@ const (
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = 0x4
)
type CGroupStats struct {
Sleeping uint64
Running uint64
Stopped uint64
Uninterruptible uint64
Io_wait uint64
}
const (
CGROUPSTATS_CMD_UNSPEC = 0x3
CGROUPSTATS_CMD_GET = 0x4
CGROUPSTATS_CMD_NEW = 0x5
CGROUPSTATS_TYPE_UNSPEC = 0x0
CGROUPSTATS_TYPE_CGROUP_STATS = 0x1
CGROUPSTATS_CMD_ATTR_UNSPEC = 0x0
CGROUPSTATS_CMD_ATTR_FD = 0x1
)
type Genlmsghdr struct {
Cmd uint8
Version uint8

View File

@@ -759,6 +759,24 @@ const (
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = 0x4
)
type CGroupStats struct {
Sleeping uint64
Running uint64
Stopped uint64
Uninterruptible uint64
Io_wait uint64
}
const (
CGROUPSTATS_CMD_UNSPEC = 0x3
CGROUPSTATS_CMD_GET = 0x4
CGROUPSTATS_CMD_NEW = 0x5
CGROUPSTATS_TYPE_UNSPEC = 0x0
CGROUPSTATS_TYPE_CGROUP_STATS = 0x1
CGROUPSTATS_CMD_ATTR_UNSPEC = 0x0
CGROUPSTATS_CMD_ATTR_FD = 0x1
)
type Genlmsghdr struct {
Cmd uint8
Version uint8

View File

@@ -754,6 +754,24 @@ const (
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = 0x4
)
type CGroupStats struct {
Sleeping uint64
Running uint64
Stopped uint64
Uninterruptible uint64
Io_wait uint64
}
const (
CGROUPSTATS_CMD_UNSPEC = 0x3
CGROUPSTATS_CMD_GET = 0x4
CGROUPSTATS_CMD_NEW = 0x5
CGROUPSTATS_TYPE_UNSPEC = 0x0
CGROUPSTATS_TYPE_CGROUP_STATS = 0x1
CGROUPSTATS_CMD_ATTR_UNSPEC = 0x0
CGROUPSTATS_CMD_ATTR_FD = 0x1
)
type Genlmsghdr struct {
Cmd uint8
Version uint8

View File

@@ -767,6 +767,24 @@ const (
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = 0x4
)
type CGroupStats struct {
Sleeping uint64
Running uint64
Stopped uint64
Uninterruptible uint64
Io_wait uint64
}
const (
CGROUPSTATS_CMD_UNSPEC = 0x3
CGROUPSTATS_CMD_GET = 0x4
CGROUPSTATS_CMD_NEW = 0x5
CGROUPSTATS_TYPE_UNSPEC = 0x0
CGROUPSTATS_TYPE_CGROUP_STATS = 0x1
CGROUPSTATS_CMD_ATTR_UNSPEC = 0x0
CGROUPSTATS_CMD_ATTR_FD = 0x1
)
type Genlmsghdr struct {
Cmd uint8
Version uint8

View File

@@ -767,6 +767,24 @@ const (
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = 0x4
)
type CGroupStats struct {
Sleeping uint64
Running uint64
Stopped uint64
Uninterruptible uint64
Io_wait uint64
}
const (
CGROUPSTATS_CMD_UNSPEC = 0x3
CGROUPSTATS_CMD_GET = 0x4
CGROUPSTATS_CMD_NEW = 0x5
CGROUPSTATS_TYPE_UNSPEC = 0x0
CGROUPSTATS_TYPE_CGROUP_STATS = 0x1
CGROUPSTATS_CMD_ATTR_UNSPEC = 0x0
CGROUPSTATS_CMD_ATTR_FD = 0x1
)
type Genlmsghdr struct {
Cmd uint8
Version uint8

View File

@@ -784,6 +784,24 @@ const (
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK = 0x4
)
type CGroupStats struct {
Sleeping uint64
Running uint64
Stopped uint64
Uninterruptible uint64
Io_wait uint64
}
const (
CGROUPSTATS_CMD_UNSPEC = 0x3
CGROUPSTATS_CMD_GET = 0x4
CGROUPSTATS_CMD_NEW = 0x5
CGROUPSTATS_TYPE_UNSPEC = 0x0
CGROUPSTATS_TYPE_CGROUP_STATS = 0x1
CGROUPSTATS_CMD_ATTR_UNSPEC = 0x0
CGROUPSTATS_CMD_ATTR_FD = 0x1
)
type Genlmsghdr struct {
Cmd uint8
Version uint8

View File

@@ -1,15 +0,0 @@
// Copyright 2014 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.
// +build windows
// +build go1.4
package windows
import "syscall"
func Unsetenv(key string) error {
// This was added in Go 1.4.
return syscall.Unsetenv(key)
}

View File

@@ -23,3 +23,7 @@ func Clearenv() {
func Environ() []string {
return syscall.Environ()
}
func Unsetenv(key string) error {
return syscall.Unsetenv(key)
}

2
vendor/gopkg.in/yaml.v2/README.md generated vendored
View File

@@ -67,6 +67,8 @@ b:
d: [3, 4]
`
// Note: struct fields must be public in order for unmarshal to
// correctly populate the data.
type T struct {
A string
B struct {