diff options
author | Christopher Speller <crspeller@gmail.com> | 2017-04-24 20:11:36 -0400 |
---|---|---|
committer | Joram Wilander <jwawilander@gmail.com> | 2017-04-24 20:11:36 -0400 |
commit | f5437632f486b7d0a0a181c58f113c86d032b02c (patch) | |
tree | 407388e3003a210a89f4b2128d7ad656f8b79d26 /vendor/golang.org/x/crypto | |
parent | 7f68a60f8c228d5604e0566bf84cabb145d16c37 (diff) | |
download | chat-f5437632f486b7d0a0a181c58f113c86d032b02c.tar.gz chat-f5437632f486b7d0a0a181c58f113c86d032b02c.tar.bz2 chat-f5437632f486b7d0a0a181c58f113c86d032b02c.zip |
Upgrading server dependancies (#6215)
Diffstat (limited to 'vendor/golang.org/x/crypto')
44 files changed, 2407 insertions, 447 deletions
diff --git a/vendor/golang.org/x/crypto/acme/acme.go b/vendor/golang.org/x/crypto/acme/acme.go index 8619508e5..d650604d3 100644 --- a/vendor/golang.org/x/crypto/acme/acme.go +++ b/vendor/golang.org/x/crypto/acme/acme.go @@ -15,6 +15,7 @@ package acme import ( "bytes" + "context" "crypto" "crypto/ecdsa" "crypto/elliptic" @@ -36,9 +37,6 @@ import ( "strings" "sync" "time" - - "golang.org/x/net/context" - "golang.org/x/net/context/ctxhttp" ) // LetsEncryptURL is the Directory endpoint of Let's Encrypt CA. @@ -133,7 +131,7 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) { if dirURL == "" { dirURL = LetsEncryptURL } - res, err := ctxhttp.Get(ctx, c.HTTPClient, dirURL) + res, err := c.get(ctx, dirURL) if err != nil { return Directory{}, err } @@ -200,7 +198,7 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, req.NotAfter = now.Add(exp).Format(time.RFC3339) } - res, err := c.postJWS(ctx, c.Key, c.dir.CertURL, req) + res, err := c.retryPostJWS(ctx, c.Key, c.dir.CertURL, req) if err != nil { return nil, "", err } @@ -216,7 +214,7 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, return cert, curl, err } // slurp issued cert and CA chain, if requested - cert, err := responseCert(ctx, c.HTTPClient, res, bundle) + cert, err := c.responseCert(ctx, res, bundle) return cert, curl, err } @@ -231,13 +229,13 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, // and has expected features. func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) { for { - res, err := ctxhttp.Get(ctx, c.HTTPClient, url) + res, err := c.get(ctx, url) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode == http.StatusOK { - return responseCert(ctx, c.HTTPClient, res, bundle) + return c.responseCert(ctx, res, bundle) } if res.StatusCode > 299 { return nil, responseError(res) @@ -275,7 +273,7 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, if key == nil { key = c.Key } - res, err := c.postJWS(ctx, key, c.dir.RevokeURL, body) + res, err := c.retryPostJWS(ctx, key, c.dir.RevokeURL, body) if err != nil { return err } @@ -363,7 +361,7 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, Resource: "new-authz", Identifier: authzID{Type: "dns", Value: domain}, } - res, err := c.postJWS(ctx, c.Key, c.dir.AuthzURL, req) + res, err := c.retryPostJWS(ctx, c.Key, c.dir.AuthzURL, req) if err != nil { return nil, err } @@ -387,7 +385,7 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, // If a caller needs to poll an authorization until its status is final, // see the WaitAuthorization method. func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) { - res, err := ctxhttp.Get(ctx, c.HTTPClient, url) + res, err := c.get(ctx, url) if err != nil { return nil, err } @@ -421,7 +419,7 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error { Status: "deactivated", Delete: true, } - res, err := c.postJWS(ctx, c.Key, url, req) + res, err := c.retryPostJWS(ctx, c.Key, url, req) if err != nil { return err } @@ -440,23 +438,9 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error { // In all other cases WaitAuthorization returns an error. // If the Status is StatusInvalid, the returned error is ErrAuthorizationFailed. func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) { - var count int - sleep := func(v string, inc int) error { - count += inc - d := backoff(count, 10*time.Second) - d = retryAfter(v, d) - wakeup := time.NewTimer(d) - defer wakeup.Stop() - select { - case <-ctx.Done(): - return ctx.Err() - case <-wakeup.C: - return nil - } - } - + sleep := sleeper(ctx) for { - res, err := ctxhttp.Get(ctx, c.HTTPClient, url) + res, err := c.get(ctx, url) if err != nil { return nil, err } @@ -493,7 +477,7 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat // // A client typically polls a challenge status using this method. func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) { - res, err := ctxhttp.Get(ctx, c.HTTPClient, url) + res, err := c.get(ctx, url) if err != nil { return nil, err } @@ -527,7 +511,7 @@ func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error Type: chal.Type, Auth: auth, } - res, err := c.postJWS(ctx, c.Key, chal.URI, req) + res, err := c.retryPostJWS(ctx, c.Key, chal.URI, req) if err != nil { return nil, err } @@ -660,7 +644,7 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun req.Contact = acct.Contact req.Agreement = acct.AgreedTerms } - res, err := c.postJWS(ctx, c.Key, url, req) + res, err := c.retryPostJWS(ctx, c.Key, url, req) if err != nil { return nil, err } @@ -697,6 +681,40 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun }, nil } +// retryPostJWS will retry calls to postJWS if there is a badNonce error, +// clearing the stored nonces after each error. +// If the response was 4XX-5XX, then responseError is called on the body, +// the body is closed, and the error returned. +func (c *Client) retryPostJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) { + sleep := sleeper(ctx) + for { + res, err := c.postJWS(ctx, key, url, body) + if err != nil { + return nil, err + } + // handle errors 4XX-5XX with responseError + if res.StatusCode >= 400 && res.StatusCode <= 599 { + err := responseError(res) + res.Body.Close() + // according to spec badNonce is urn:ietf:params:acme:error:badNonce + // however, acme servers in the wild return their version of the error + // https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4 + if ae, ok := err.(*Error); ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce") { + // clear any nonces that we might've stored that might now be + // considered bad + c.clearNonces() + retry := res.Header.Get("retry-after") + if err := sleep(retry, 1); err != nil { + return nil, err + } + continue + } + return nil, err + } + return res, nil + } +} + // postJWS signs the body with the given key and POSTs it to the provided url. // The body argument must be JSON-serializable. func (c *Client) postJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) { @@ -708,7 +726,7 @@ func (c *Client) postJWS(ctx context.Context, key crypto.Signer, url string, bod if err != nil { return nil, err } - res, err := ctxhttp.Post(ctx, c.HTTPClient, url, "application/jose+json", bytes.NewReader(b)) + res, err := c.post(ctx, url, "application/jose+json", bytes.NewReader(b)) if err != nil { return nil, err } @@ -722,7 +740,7 @@ func (c *Client) popNonce(ctx context.Context, url string) (string, error) { c.noncesMu.Lock() defer c.noncesMu.Unlock() if len(c.nonces) == 0 { - return fetchNonce(ctx, c.HTTPClient, url) + return c.fetchNonce(ctx, url) } var nonce string for nonce = range c.nonces { @@ -732,6 +750,13 @@ func (c *Client) popNonce(ctx context.Context, url string) (string, error) { return nonce, nil } +// clearNonces clears any stored nonces +func (c *Client) clearNonces() { + c.noncesMu.Lock() + defer c.noncesMu.Unlock() + c.nonces = make(map[string]struct{}) +} + // addNonce stores a nonce value found in h (if any) for future use. func (c *Client) addNonce(h http.Header) { v := nonceFromHeader(h) @@ -749,8 +774,58 @@ func (c *Client) addNonce(h http.Header) { c.nonces[v] = struct{}{} } -func fetchNonce(ctx context.Context, client *http.Client, url string) (string, error) { - resp, err := ctxhttp.Head(ctx, client, url) +func (c *Client) httpClient() *http.Client { + if c.HTTPClient != nil { + return c.HTTPClient + } + return http.DefaultClient +} + +func (c *Client) get(ctx context.Context, urlStr string) (*http.Response, error) { + req, err := http.NewRequest("GET", urlStr, nil) + if err != nil { + return nil, err + } + return c.do(ctx, req) +} + +func (c *Client) head(ctx context.Context, urlStr string) (*http.Response, error) { + req, err := http.NewRequest("HEAD", urlStr, nil) + if err != nil { + return nil, err + } + return c.do(ctx, req) +} + +func (c *Client) post(ctx context.Context, urlStr, contentType string, body io.Reader) (*http.Response, error) { + req, err := http.NewRequest("POST", urlStr, body) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", contentType) + return c.do(ctx, req) +} + +func (c *Client) do(ctx context.Context, req *http.Request) (*http.Response, error) { + res, err := c.httpClient().Do(req.WithContext(ctx)) + if err != nil { + select { + case <-ctx.Done(): + // Prefer the unadorned context error. + // (The acme package had tests assuming this, previously from ctxhttp's + // behavior, predating net/http supporting contexts natively) + // TODO(bradfitz): reconsider this in the future. But for now this + // requires no test updates. + return nil, ctx.Err() + default: + return nil, err + } + } + return res, nil +} + +func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) { + resp, err := c.head(ctx, url) if err != nil { return "", err } @@ -769,7 +844,7 @@ func nonceFromHeader(h http.Header) string { return h.Get("Replay-Nonce") } -func responseCert(ctx context.Context, client *http.Client, res *http.Response, bundle bool) ([][]byte, error) { +func (c *Client) responseCert(ctx context.Context, res *http.Response, bundle bool) ([][]byte, error) { b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1)) if err != nil { return nil, fmt.Errorf("acme: response stream: %v", err) @@ -793,7 +868,7 @@ func responseCert(ctx context.Context, client *http.Client, res *http.Response, return nil, errors.New("acme: rel=up link is too large") } for _, url := range up { - cc, err := chainCert(ctx, client, url, 0) + cc, err := c.chainCert(ctx, url, 0) if err != nil { return nil, err } @@ -836,12 +911,12 @@ func responseError(resp *http.Response) error { // if the recursion level reaches maxChainLen. // // First chainCert call starts with depth of 0. -func chainCert(ctx context.Context, client *http.Client, url string, depth int) ([][]byte, error) { +func (c *Client) chainCert(ctx context.Context, url string, depth int) ([][]byte, error) { if depth >= maxChainLen { return nil, errors.New("acme: certificate chain is too deep") } - res, err := ctxhttp.Get(ctx, client, url) + res, err := c.get(ctx, url) if err != nil { return nil, err } @@ -863,7 +938,7 @@ func chainCert(ctx context.Context, client *http.Client, url string, depth int) return nil, errors.New("acme: certificate chain is too large") } for _, up := range uplink { - cc, err := chainCert(ctx, client, up, depth+1) + cc, err := c.chainCert(ctx, up, depth+1) if err != nil { return nil, err } @@ -893,6 +968,28 @@ func linkHeader(h http.Header, rel string) []string { return links } +// sleeper returns a function that accepts the Retry-After HTTP header value +// and an increment that's used with backoff to increasingly sleep on +// consecutive calls until the context is done. If the Retry-After header +// cannot be parsed, then backoff is used with a maximum sleep time of 10 +// seconds. +func sleeper(ctx context.Context) func(ra string, inc int) error { + var count int + return func(ra string, inc int) error { + count += inc + d := backoff(count, 10*time.Second) + d = retryAfter(ra, d) + wakeup := time.NewTimer(d) + defer wakeup.Stop() + select { + case <-ctx.Done(): + return ctx.Err() + case <-wakeup.C: + return nil + } + } +} + // retryAfter parses a Retry-After HTTP header value, // trying to convert v into an int (seconds) or use http.ParseTime otherwise. // It returns d if v cannot be parsed. @@ -974,7 +1071,8 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) { NotBefore: time.Now(), NotAfter: time.Now().Add(24 * time.Hour), BasicConstraintsValid: true, - KeyUsage: x509.KeyUsageKeyEncipherment, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, } } tmpl.DNSNames = san diff --git a/vendor/golang.org/x/crypto/acme/acme_test.go b/vendor/golang.org/x/crypto/acme/acme_test.go index 1205dbb36..0210ce3df 100644 --- a/vendor/golang.org/x/crypto/acme/acme_test.go +++ b/vendor/golang.org/x/crypto/acme/acme_test.go @@ -6,6 +6,7 @@ package acme import ( "bytes" + "context" "crypto/rand" "crypto/rsa" "crypto/tls" @@ -23,8 +24,6 @@ import ( "strings" "testing" "time" - - "golang.org/x/net/context" ) // Decodes a JWS-encoded request and unmarshals the decoded JSON into a provided @@ -981,7 +980,8 @@ func TestNonce_fetch(t *testing.T) { defer ts.Close() for ; i < len(tests); i++ { test := tests[i] - n, err := fetchNonce(context.Background(), http.DefaultClient, ts.URL) + c := &Client{} + n, err := c.fetchNonce(context.Background(), ts.URL) if n != test.nonce { t.Errorf("%d: n=%q; want %q", i, n, test.nonce) } @@ -999,7 +999,8 @@ func TestNonce_fetchError(t *testing.T) { w.WriteHeader(http.StatusTooManyRequests) })) defer ts.Close() - _, err := fetchNonce(context.Background(), http.DefaultClient, ts.URL) + c := &Client{} + _, err := c.fetchNonce(context.Background(), ts.URL) e, ok := err.(*Error) if !ok { t.Fatalf("err is %T; want *Error", err) @@ -1064,6 +1065,44 @@ func TestNonce_postJWS(t *testing.T) { } } +func TestRetryPostJWS(t *testing.T) { + var count int + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + count++ + w.Header().Set("replay-nonce", fmt.Sprintf("nonce%d", count)) + if r.Method == "HEAD" { + // We expect the client to do 2 head requests to fetch + // nonces, one to start and another after getting badNonce + return + } + + head, err := decodeJWSHead(r) + if err != nil { + t.Errorf("decodeJWSHead: %v", err) + } else if head.Nonce == "" { + t.Error("head.Nonce is empty") + } else if head.Nonce == "nonce1" { + // return a badNonce error to force the call to retry + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(`{"type":"urn:ietf:params:acme:error:badNonce"}`)) + return + } + // Make client.Authorize happy; we're not testing its result. + w.WriteHeader(http.StatusCreated) + w.Write([]byte(`{"status":"valid"}`)) + })) + defer ts.Close() + + client := Client{Key: testKey, dir: &Directory{AuthzURL: ts.URL}} + // This call will fail with badNonce, causing a retry + if _, err := client.Authorize(context.Background(), "example.com"); err != nil { + t.Errorf("client.Authorize 1: %v", err) + } + if count != 4 { + t.Errorf("total requests count: %d; want 4", count) + } +} + func TestLinkHeader(t *testing.T) { h := http.Header{"Link": { `<https://example.com/acme/new-authz>;rel="next"`, diff --git a/vendor/golang.org/x/crypto/acme/autocert/autocert.go b/vendor/golang.org/x/crypto/acme/autocert/autocert.go index 4b15816ae..98842b457 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/autocert.go +++ b/vendor/golang.org/x/crypto/acme/autocert/autocert.go @@ -10,6 +10,7 @@ package autocert import ( "bytes" + "context" "crypto" "crypto/ecdsa" "crypto/elliptic" @@ -30,7 +31,6 @@ import ( "time" "golang.org/x/crypto/acme" - "golang.org/x/net/context" ) // pseudoRand is safe for concurrent use. @@ -41,8 +41,9 @@ func init() { pseudoRand = &lockedMathRand{rnd: mathrand.New(src)} } -// AcceptTOS always returns true to indicate the acceptance of a CA Terms of Service -// during account registration. +// AcceptTOS is a Manager.Prompt function that always returns true to +// indicate acceptance of the CA's Terms of Service during account +// registration. func AcceptTOS(tosURL string) bool { return true } // HostPolicy specifies which host names the Manager is allowed to respond to. @@ -76,18 +77,6 @@ func defaultHostPolicy(context.Context, string) error { // It obtains and refreshes certificates automatically, // as well as providing them to a TLS server via tls.Config. // -// A simple usage example: -// -// m := autocert.Manager{ -// Prompt: autocert.AcceptTOS, -// HostPolicy: autocert.HostWhitelist("example.org"), -// } -// s := &http.Server{ -// Addr: ":https", -// TLSConfig: &tls.Config{GetCertificate: m.GetCertificate}, -// } -// s.ListenAndServeTLS("", "") -// // To preserve issued certificates and improve overall performance, // use a cache implementation of Cache. For instance, DirCache. type Manager struct { @@ -123,7 +112,7 @@ type Manager struct { // RenewBefore optionally specifies how early certificates should // be renewed before they expire. // - // If zero, they're renewed 1 week before expiration. + // If zero, they're renewed 30 days before expiration. RenewBefore time.Duration // Client is used to perform low-level operations, such as account registration @@ -173,11 +162,18 @@ type Manager struct { // The error is propagated back to the caller of GetCertificate and is user-visible. // This does not affect cached certs. See HostPolicy field description for more details. func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { + if m.Prompt == nil { + return nil, errors.New("acme/autocert: Manager.Prompt not set") + } + name := hello.ServerName if name == "" { return nil, errors.New("acme/autocert: missing server name") } + 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() @@ -185,7 +181,7 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, if cert := m.tokenCert[name]; cert != nil { return cert, nil } - if cert, err := m.cacheGet(name); err == nil { + if cert, err := m.cacheGet(ctx, name); err == nil { return cert, nil } // TODO: cache error results? @@ -194,7 +190,7 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, // regular domain name = strings.TrimSuffix(name, ".") // golang.org/issue/18114 - cert, err := m.cert(name) + cert, err := m.cert(ctx, name) if err == nil { return cert, nil } @@ -203,7 +199,6 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, } // first-time - ctx := context.Background() // TODO: use a deadline? if err := m.hostPolicy()(ctx, name); err != nil { return nil, err } @@ -211,14 +206,14 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, if err != nil { return nil, err } - m.cachePut(name, cert) + m.cachePut(ctx, name, cert) return cert, nil } // 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. -func (m *Manager) cert(name string) (*tls.Certificate, error) { +func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, error) { m.stateMu.Lock() if s, ok := m.state[name]; ok { m.stateMu.Unlock() @@ -227,7 +222,7 @@ func (m *Manager) cert(name string) (*tls.Certificate, error) { return s.tlscert() } defer m.stateMu.Unlock() - cert, err := m.cacheGet(name) + cert, err := m.cacheGet(ctx, name) if err != nil { return nil, err } @@ -249,12 +244,10 @@ func (m *Manager) cert(name string) (*tls.Certificate, error) { } // cacheGet always returns a valid certificate, or an error otherwise. -func (m *Manager) cacheGet(domain string) (*tls.Certificate, error) { +func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate, error) { if m.Cache == nil { return nil, ErrCacheMiss } - // TODO: might want to define a cache timeout on m - ctx := context.Background() data, err := m.Cache.Get(ctx, domain) if err != nil { return nil, err @@ -297,7 +290,7 @@ func (m *Manager) cacheGet(domain string) (*tls.Certificate, error) { return tlscert, nil } -func (m *Manager) cachePut(domain string, tlscert *tls.Certificate) error { +func (m *Manager) cachePut(ctx context.Context, domain string, tlscert *tls.Certificate) error { if m.Cache == nil { return nil } @@ -329,8 +322,6 @@ func (m *Manager) cachePut(domain string, tlscert *tls.Certificate) error { } } - // TODO: might want to define a cache timeout on m - ctx := context.Background() return m.Cache.Put(ctx, domain, buf.Bytes()) } @@ -494,7 +485,7 @@ func (m *Manager) verify(ctx context.Context, domain string) error { if err != nil { return err } - m.putTokenCert(name, &cert) + m.putTokenCert(ctx, name, &cert) defer func() { // verification has ended at this point // don't need token cert anymore @@ -512,14 +503,14 @@ func (m *Manager) verify(ctx context.Context, domain string) error { // putTokenCert stores the cert under the named key in both m.tokenCert map // and m.Cache. -func (m *Manager) putTokenCert(name string, cert *tls.Certificate) { +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) } m.tokenCert[name] = cert - m.cachePut(name, cert) + m.cachePut(ctx, name, cert) } // deleteTokenCert removes the token certificate for the specified domain name @@ -644,10 +635,10 @@ func (m *Manager) hostPolicy() HostPolicy { } func (m *Manager) renewBefore() time.Duration { - if m.RenewBefore > maxRandRenew { + if m.RenewBefore > renewJitter { return m.RenewBefore } - return 7 * 24 * time.Hour // 1 week + return 720 * time.Hour // 30 days } // certState is ready when its mutex is unlocked for reading. diff --git a/vendor/golang.org/x/crypto/acme/autocert/autocert_test.go b/vendor/golang.org/x/crypto/acme/autocert/autocert_test.go index 7afb21331..6df4cf3bf 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/autocert_test.go +++ b/vendor/golang.org/x/crypto/acme/autocert/autocert_test.go @@ -5,6 +5,7 @@ package autocert import ( + "context" "crypto" "crypto/ecdsa" "crypto/elliptic" @@ -27,7 +28,6 @@ import ( "time" "golang.org/x/crypto/acme" - "golang.org/x/net/context" ) var discoTmpl = template.Must(template.New("disco").Parse(`{ @@ -150,7 +150,7 @@ func TestGetCertificate_ForceRSA(t *testing.T) { hello := &tls.ClientHelloInfo{ServerName: "example.org"} testGetCertificate(t, man, "example.org", hello) - cert, err := man.cacheGet("example.org") + cert, err := man.cacheGet(context.Background(), "example.org") if err != nil { t.Fatalf("man.cacheGet: %v", err) } @@ -159,9 +159,28 @@ func TestGetCertificate_ForceRSA(t *testing.T) { } } -// tests man.GetCertificate flow using the provided hello argument. +func TestGetCertificate_nilPrompt(t *testing.T) { + man := &Manager{} + defer man.stopRenew() + url, finish := startACMEServerStub(t, man, "example.org") + defer finish() + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + man.Client = &acme.Client{ + Key: key, + DirectoryURL: url, + } + hello := &tls.ClientHelloInfo{ServerName: "example.org"} + if _, err := man.GetCertificate(hello); err == nil { + t.Error("got certificate for example.org; wanted error") + } +} + +// startACMEServerStub runs an ACME server // The domain argument is the expected domain name of a certificate request. -func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.ClientHelloInfo) { +func startACMEServerStub(t *testing.T, man *Manager, domain string) (url string, finish func()) { // echo token-02 | shasum -a 256 // then divide result in 2 parts separated by dot tokenCertName := "4e8eb87631187e9ff2153b56b13a4dec.13a35d002e485d60ff37354b32f665d9.token.acme.invalid" @@ -187,7 +206,7 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl // discovery case "/": if err := discoTmpl.Execute(w, ca.URL); err != nil { - t.Fatalf("discoTmpl: %v", err) + t.Errorf("discoTmpl: %v", err) } // client key registration case "/new-reg": @@ -197,7 +216,7 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl w.Header().Set("location", ca.URL+"/authz/1") w.WriteHeader(http.StatusCreated) if err := authzTmpl.Execute(w, ca.URL); err != nil { - t.Fatalf("authzTmpl: %v", err) + t.Errorf("authzTmpl: %v", err) } // accept tls-sni-02 challenge case "/challenge/2": @@ -215,14 +234,14 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl b, _ := base64.RawURLEncoding.DecodeString(req.CSR) csr, err := x509.ParseCertificateRequest(b) if err != nil { - t.Fatalf("new-cert: CSR: %v", err) + t.Errorf("new-cert: CSR: %v", err) } if csr.Subject.CommonName != domain { t.Errorf("CommonName in CSR = %q; want %q", csr.Subject.CommonName, domain) } der, err := dummyCert(csr.PublicKey, domain) if err != nil { - t.Fatalf("new-cert: dummyCert: %v", err) + t.Errorf("new-cert: dummyCert: %v", err) } chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL) w.Header().Set("link", chainUp) @@ -232,14 +251,51 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl case "/ca-cert": der, err := dummyCert(nil, "ca") if err != nil { - t.Fatalf("ca-cert: dummyCert: %v", err) + t.Errorf("ca-cert: dummyCert: %v", err) } w.Write(der) default: t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path) } })) - defer ca.Close() + finish = func() { + ca.Close() + + // make sure token cert was removed + cancel := make(chan struct{}) + done := make(chan struct{}) + go func() { + defer close(done) + tick := time.NewTicker(100 * time.Millisecond) + defer tick.Stop() + for { + hello := &tls.ClientHelloInfo{ServerName: tokenCertName} + if _, err := man.GetCertificate(hello); err != nil { + return + } + select { + case <-tick.C: + case <-cancel: + return + } + } + }() + select { + case <-done: + case <-time.After(5 * time.Second): + close(cancel) + t.Error("token cert was not removed") + <-done + } + } + return ca.URL, finish +} + +// tests man.GetCertificate flow using the provided hello argument. +// The domain argument is the expected domain name of a certificate request. +func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.ClientHelloInfo) { + url, finish := startACMEServerStub(t, man, domain) + defer finish() // use EC key to run faster on 386 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) @@ -248,7 +304,7 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl } man.Client = &acme.Client{ Key: key, - DirectoryURL: ca.URL, + DirectoryURL: url, } // simulate tls.Config.GetCertificate @@ -279,23 +335,6 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl t.Errorf("cert.DNSNames = %v; want %q", cert.DNSNames, domain) } - // make sure token cert was removed - done = make(chan struct{}) - go func() { - for { - hello := &tls.ClientHelloInfo{ServerName: tokenCertName} - if _, err := man.GetCertificate(hello); err != nil { - break - } - time.Sleep(100 * time.Millisecond) - } - close(done) - }() - select { - case <-time.After(5 * time.Second): - t.Error("token cert was not removed") - case <-done: - } } func TestAccountKeyCache(t *testing.T) { @@ -335,10 +374,11 @@ func TestCache(t *testing.T) { man := &Manager{Cache: newMemCache()} defer man.stopRenew() - if err := man.cachePut("example.org", tlscert); err != nil { + ctx := context.Background() + if err := man.cachePut(ctx, "example.org", tlscert); err != nil { t.Fatalf("man.cachePut: %v", err) } - res, err := man.cacheGet("example.org") + res, err := man.cacheGet(ctx, "example.org") if err != nil { t.Fatalf("man.cacheGet: %v", err) } diff --git a/vendor/golang.org/x/crypto/acme/autocert/cache.go b/vendor/golang.org/x/crypto/acme/autocert/cache.go index 9b184aabe..61a5fd239 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/cache.go +++ b/vendor/golang.org/x/crypto/acme/autocert/cache.go @@ -5,12 +5,11 @@ package autocert import ( + "context" "errors" "io/ioutil" "os" "path/filepath" - - "golang.org/x/net/context" ) // ErrCacheMiss is returned when a certificate is not found in cache. @@ -78,12 +77,13 @@ func (d DirCache) Put(ctx context.Context, name string, data []byte) error { if tmp, err = d.writeTempFile(name, data); err != nil { return } - // prevent overwriting the file if the context was cancelled - if ctx.Err() != nil { - return // no need to set err + select { + case <-ctx.Done(): + // Don't overwrite the file if the context was canceled. + default: + newName := filepath.Join(string(d), name) + err = os.Rename(tmp, newName) } - name = filepath.Join(string(d), name) - err = os.Rename(tmp, name) }() select { case <-ctx.Done(): diff --git a/vendor/golang.org/x/crypto/acme/autocert/cache_test.go b/vendor/golang.org/x/crypto/acme/autocert/cache_test.go index ad6d4a464..6e1b88d57 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/cache_test.go +++ b/vendor/golang.org/x/crypto/acme/autocert/cache_test.go @@ -5,13 +5,12 @@ package autocert import ( + "context" "io/ioutil" "os" "path/filepath" "reflect" "testing" - - "golang.org/x/net/context" ) // make sure DirCache satisfies Cache interface diff --git a/vendor/golang.org/x/crypto/acme/autocert/example_test.go b/vendor/golang.org/x/crypto/acme/autocert/example_test.go new file mode 100644 index 000000000..c6267b8dd --- /dev/null +++ b/vendor/golang.org/x/crypto/acme/autocert/example_test.go @@ -0,0 +1,34 @@ +// Copyright 2017 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 autocert_test + +import ( + "crypto/tls" + "fmt" + "log" + "net/http" + + "golang.org/x/crypto/acme/autocert" +) + +func ExampleNewListener() { + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, TLS user! Your config: %+v", r.TLS) + }) + log.Fatal(http.Serve(autocert.NewListener("example.com"), mux)) +} + +func ExampleManager() { + m := autocert.Manager{ + Prompt: autocert.AcceptTOS, + HostPolicy: autocert.HostWhitelist("example.org"), + } + s := &http.Server{ + Addr: ":https", + TLSConfig: &tls.Config{GetCertificate: m.GetCertificate}, + } + s.ListenAndServeTLS("", "") +} diff --git a/vendor/golang.org/x/crypto/acme/autocert/listener.go b/vendor/golang.org/x/crypto/acme/autocert/listener.go new file mode 100644 index 000000000..d4c93d2f1 --- /dev/null +++ b/vendor/golang.org/x/crypto/acme/autocert/listener.go @@ -0,0 +1,153 @@ +// Copyright 2017 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 autocert + +import ( + "crypto/tls" + "log" + "net" + "os" + "path/filepath" + "runtime" + "time" +) + +// NewListener returns a net.Listener that listens on the standard TLS +// port (443) on all interfaces and returns *tls.Conn connections with +// LetsEncrypt certificates for the provided domain or domains. +// +// It enables one-line HTTPS servers: +// +// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler)) +// +// NewListener is a convenience function for a common configuration. +// More complex or custom configurations can use the autocert.Manager +// type instead. +// +// Use of this function implies acceptance of the LetsEncrypt Terms of +// Service. If domains is not empty, the provided domains are passed +// to HostWhitelist. If domains is empty, the listener will do +// LetsEncrypt challenges for any requested domain, which is not +// recommended. +// +// Certificates are cached in a "golang-autocert" directory under an +// operating system-specific cache or temp directory. This may not +// be suitable for servers spanning multiple machines. +// +// The returned Listener also enables TCP keep-alives on the accepted +// connections. The returned *tls.Conn are returned before their TLS +// handshake has completed. +func NewListener(domains ...string) net.Listener { + m := &Manager{ + Prompt: AcceptTOS, + } + if len(domains) > 0 { + m.HostPolicy = HostWhitelist(domains...) + } + dir := cacheDir() + if err := os.MkdirAll(dir, 0700); err != nil { + log.Printf("warning: autocert.NewListener not using a cache: %v", err) + } else { + m.Cache = DirCache(dir) + } + return m.Listener() +} + +// Listener listens on the standard TLS port (443) on all interfaces +// and returns a net.Listener returning *tls.Conn connections. +// +// The returned Listener also enables TCP keep-alives on the accepted +// connections. The returned *tls.Conn are returned before their TLS +// handshake has completed. +// +// Unlike NewListener, it is the caller's responsibility to initialize +// the Manager m's Prompt, Cache, HostPolicy, and other desired options. +func (m *Manager) Listener() net.Listener { + ln := &listener{ + m: m, + conf: &tls.Config{ + GetCertificate: m.GetCertificate, // bonus: panic on nil m + }, + } + ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443") + return ln +} + +type listener struct { + m *Manager + conf *tls.Config + + tcpListener net.Listener + tcpListenErr error +} + +func (ln *listener) Accept() (net.Conn, error) { + if ln.tcpListenErr != nil { + return nil, ln.tcpListenErr + } + conn, err := ln.tcpListener.Accept() + if err != nil { + return nil, err + } + tcpConn := conn.(*net.TCPConn) + + // Because Listener is a convenience function, help out with + // this too. This is not possible for the caller to set once + // we return a *tcp.Conn wrapping an inaccessible net.Conn. + // If callers don't want this, they can do things the manual + // way and tweak as needed. But this is what net/http does + // itself, so copy that. If net/http changes, we can change + // here too. + tcpConn.SetKeepAlive(true) + tcpConn.SetKeepAlivePeriod(3 * time.Minute) + + return tls.Server(tcpConn, ln.conf), nil +} + +func (ln *listener) Addr() net.Addr { + if ln.tcpListener != nil { + return ln.tcpListener.Addr() + } + // net.Listen failed. Return something non-nil in case callers + // call Addr before Accept: + return &net.TCPAddr{IP: net.IP{0, 0, 0, 0}, Port: 443} +} + +func (ln *listener) Close() error { + if ln.tcpListenErr != nil { + return ln.tcpListenErr + } + return ln.tcpListener.Close() +} + +func homeDir() string { + if runtime.GOOS == "windows" { + return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") + } + if h := os.Getenv("HOME"); h != "" { + return h + } + return "/" +} + +func cacheDir() string { + const base = "golang-autocert" + switch runtime.GOOS { + case "darwin": + return filepath.Join(homeDir(), "Library", "Caches", base) + case "windows": + for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} { + if v := os.Getenv(ev); v != "" { + return filepath.Join(v, base) + } + } + // Worst case: + return filepath.Join(homeDir(), base) + } + if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" { + return filepath.Join(xdg, base) + } + return filepath.Join(homeDir(), ".cache", base) +} diff --git a/vendor/golang.org/x/crypto/acme/autocert/renewal.go b/vendor/golang.org/x/crypto/acme/autocert/renewal.go index 1a5018c8b..6c5da2bc8 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/renewal.go +++ b/vendor/golang.org/x/crypto/acme/autocert/renewal.go @@ -5,15 +5,14 @@ package autocert import ( + "context" "crypto" "sync" "time" - - "golang.org/x/net/context" ) -// maxRandRenew is a maximum deviation from Manager.RenewBefore. -const maxRandRenew = time.Hour +// renewJitter is the maximum deviation from Manager.RenewBefore. +const renewJitter = time.Hour // domainRenewal tracks the state used by the periodic timers // renewing a single domain's cert. @@ -65,7 +64,7 @@ func (dr *domainRenewal) renew() { // TODO: rotate dr.key at some point? next, err := dr.do(ctx) if err != nil { - next = maxRandRenew / 2 + next = renewJitter / 2 next += time.Duration(pseudoRand.int63n(int64(next))) } dr.timer = time.AfterFunc(next, dr.renew) @@ -83,9 +82,9 @@ func (dr *domainRenewal) renew() { func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) { // a race is likely unavoidable in a distributed environment // but we try nonetheless - if tlscert, err := dr.m.cacheGet(dr.domain); err == nil { + if tlscert, err := dr.m.cacheGet(ctx, dr.domain); err == nil { next := dr.next(tlscert.Leaf.NotAfter) - if next > dr.m.renewBefore()+maxRandRenew { + if next > dr.m.renewBefore()+renewJitter { return next, nil } } @@ -103,7 +102,7 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) { if err != nil { return 0, err } - dr.m.cachePut(dr.domain, tlscert) + dr.m.cachePut(ctx, dr.domain, tlscert) dr.m.stateMu.Lock() defer dr.m.stateMu.Unlock() // m.state is guaranteed to be non-nil at this point @@ -114,7 +113,7 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) { func (dr *domainRenewal) next(expiry time.Time) time.Duration { d := expiry.Sub(timeNow()) - dr.m.renewBefore() // add a bit of randomness to renew deadline - n := pseudoRand.int63n(int64(maxRandRenew)) + n := pseudoRand.int63n(int64(renewJitter)) d -= time.Duration(n) if d < 0 { return 0 diff --git a/vendor/golang.org/x/crypto/acme/autocert/renewal_test.go b/vendor/golang.org/x/crypto/acme/autocert/renewal_test.go index 10c811ac4..f232619c2 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/renewal_test.go +++ b/vendor/golang.org/x/crypto/acme/autocert/renewal_test.go @@ -5,6 +5,7 @@ package autocert import ( + "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -31,7 +32,7 @@ func TestRenewalNext(t *testing.T) { expiry time.Time min, max time.Duration }{ - {now.Add(90 * 24 * time.Hour), 83*24*time.Hour - maxRandRenew, 83 * 24 * time.Hour}, + {now.Add(90 * 24 * time.Hour), 83*24*time.Hour - renewJitter, 83 * 24 * time.Hour}, {now.Add(time.Hour), 0, 1}, {now, 0, 1}, {now.Add(-time.Hour), 0, 1}, @@ -127,7 +128,7 @@ func TestRenewFromCache(t *testing.T) { t.Fatal(err) } tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}} - if err := man.cachePut(domain, tlscert); err != nil { + if err := man.cachePut(context.Background(), domain, tlscert); err != nil { t.Fatal(err) } @@ -151,7 +152,7 @@ func TestRenewFromCache(t *testing.T) { // ensure the new cert is cached after := time.Now().Add(future) - tlscert, err := man.cacheGet(domain) + tlscert, err := man.cacheGet(context.Background(), domain) if err != nil { t.Fatalf("man.cacheGet: %v", err) } diff --git a/vendor/golang.org/x/crypto/blake2b/blake2b.go b/vendor/golang.org/x/crypto/blake2b/blake2b.go index fa9e48e31..ce6224101 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2b.go +++ b/vendor/golang.org/x/crypto/blake2b/blake2b.go @@ -4,7 +4,7 @@ // Package blake2b implements the BLAKE2b hash algorithm as // defined in RFC 7693. -package blake2b +package blake2b // import "golang.org/x/crypto/blake2b" import ( "encoding/binary" diff --git a/vendor/golang.org/x/crypto/blake2s/blake2s.go b/vendor/golang.org/x/crypto/blake2s/blake2s.go index 394c12154..7e3fc4fd8 100644 --- a/vendor/golang.org/x/crypto/blake2s/blake2s.go +++ b/vendor/golang.org/x/crypto/blake2s/blake2s.go @@ -4,7 +4,7 @@ // Package blake2s implements the BLAKE2s hash algorithm as // defined in RFC 7693. -package blake2s +package blake2s // import "golang.org/x/crypto/blake2s" import ( "encoding/binary" diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go index eb6739a10..3f0dcb9d8 100644 --- a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go +++ b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD as specified in RFC 7539. -package chacha20poly1305 +package chacha20poly1305 // import "golang.org/x/crypto/chacha20poly1305" import ( "crypto/cipher" diff --git a/vendor/golang.org/x/crypto/cryptobyte/string.go b/vendor/golang.org/x/crypto/cryptobyte/string.go index b1215b3bf..678033611 100644 --- a/vendor/golang.org/x/crypto/cryptobyte/string.go +++ b/vendor/golang.org/x/crypto/cryptobyte/string.go @@ -5,7 +5,7 @@ // Package cryptobyte implements building and parsing of byte strings for // DER-encoded ASN.1 and TLS messages. See the examples for the Builder and // String types to get started. -package cryptobyte +package cryptobyte // import "golang.org/x/crypto/cryptobyte" // String represents a string of bytes. It provides methods for parsing // fixed-length and length-prefixed values from it. diff --git a/vendor/golang.org/x/crypto/curve25519/cswap_amd64.s b/vendor/golang.org/x/crypto/curve25519/cswap_amd64.s index 45484d1b5..cd793a5b5 100644 --- a/vendor/golang.org/x/crypto/curve25519/cswap_amd64.s +++ b/vendor/golang.org/x/crypto/curve25519/cswap_amd64.s @@ -2,87 +2,64 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This code was translated into a form compatible with 6a from the public -// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html - // +build amd64,!gccgo,!appengine -// func cswap(inout *[5]uint64, v uint64) +// func cswap(inout *[4][5]uint64, v uint64) TEXT ·cswap(SB),7,$0 MOVQ inout+0(FP),DI MOVQ v+8(FP),SI - CMPQ SI,$1 - MOVQ 0(DI),SI - MOVQ 80(DI),DX - MOVQ 8(DI),CX - MOVQ 88(DI),R8 - MOVQ SI,R9 - CMOVQEQ DX,SI - CMOVQEQ R9,DX - MOVQ CX,R9 - CMOVQEQ R8,CX - CMOVQEQ R9,R8 - MOVQ SI,0(DI) - MOVQ DX,80(DI) - MOVQ CX,8(DI) - MOVQ R8,88(DI) - MOVQ 16(DI),SI - MOVQ 96(DI),DX - MOVQ 24(DI),CX - MOVQ 104(DI),R8 - MOVQ SI,R9 - CMOVQEQ DX,SI - CMOVQEQ R9,DX - MOVQ CX,R9 - CMOVQEQ R8,CX - CMOVQEQ R9,R8 - MOVQ SI,16(DI) - MOVQ DX,96(DI) - MOVQ CX,24(DI) - MOVQ R8,104(DI) - MOVQ 32(DI),SI - MOVQ 112(DI),DX - MOVQ 40(DI),CX - MOVQ 120(DI),R8 - MOVQ SI,R9 - CMOVQEQ DX,SI - CMOVQEQ R9,DX - MOVQ CX,R9 - CMOVQEQ R8,CX - CMOVQEQ R9,R8 - MOVQ SI,32(DI) - MOVQ DX,112(DI) - MOVQ CX,40(DI) - MOVQ R8,120(DI) - MOVQ 48(DI),SI - MOVQ 128(DI),DX - MOVQ 56(DI),CX - MOVQ 136(DI),R8 - MOVQ SI,R9 - CMOVQEQ DX,SI - CMOVQEQ R9,DX - MOVQ CX,R9 - CMOVQEQ R8,CX - CMOVQEQ R9,R8 - MOVQ SI,48(DI) - MOVQ DX,128(DI) - MOVQ CX,56(DI) - MOVQ R8,136(DI) - MOVQ 64(DI),SI - MOVQ 144(DI),DX - MOVQ 72(DI),CX - MOVQ 152(DI),R8 - MOVQ SI,R9 - CMOVQEQ DX,SI - CMOVQEQ R9,DX - MOVQ CX,R9 - CMOVQEQ R8,CX - CMOVQEQ R9,R8 - MOVQ SI,64(DI) - MOVQ DX,144(DI) - MOVQ CX,72(DI) - MOVQ R8,152(DI) - MOVQ DI,AX - MOVQ SI,DX + SUBQ $1, SI + NOTQ SI + MOVQ SI, X15 + PSHUFD $0x44, X15, X15 + + MOVOU 0(DI), X0 + MOVOU 16(DI), X2 + MOVOU 32(DI), X4 + MOVOU 48(DI), X6 + MOVOU 64(DI), X8 + MOVOU 80(DI), X1 + MOVOU 96(DI), X3 + MOVOU 112(DI), X5 + MOVOU 128(DI), X7 + MOVOU 144(DI), X9 + + MOVO X1, X10 + MOVO X3, X11 + MOVO X5, X12 + MOVO X7, X13 + MOVO X9, X14 + + PXOR X0, X10 + PXOR X2, X11 + PXOR X4, X12 + PXOR X6, X13 + PXOR X8, X14 + PAND X15, X10 + PAND X15, X11 + PAND X15, X12 + PAND X15, X13 + PAND X15, X14 + PXOR X10, X0 + PXOR X10, X1 + PXOR X11, X2 + PXOR X11, X3 + PXOR X12, X4 + PXOR X12, X5 + PXOR X13, X6 + PXOR X13, X7 + PXOR X14, X8 + PXOR X14, X9 + + MOVOU X0, 0(DI) + MOVOU X2, 16(DI) + MOVOU X4, 32(DI) + MOVOU X6, 48(DI) + MOVOU X8, 64(DI) + MOVOU X1, 80(DI) + MOVOU X3, 96(DI) + MOVOU X5, 112(DI) + MOVOU X7, 128(DI) + MOVOU X9, 144(DI) RET diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519.go b/vendor/golang.org/x/crypto/curve25519/curve25519.go index 6918c47fc..2d14c2a78 100644 --- a/vendor/golang.org/x/crypto/curve25519/curve25519.go +++ b/vendor/golang.org/x/crypto/curve25519/curve25519.go @@ -8,6 +8,10 @@ package curve25519 +import ( + "encoding/binary" +) + // This code is a port of the public domain, "ref10" implementation of // curve25519 from SUPERCOP 20130419 by D. J. Bernstein. @@ -50,17 +54,11 @@ func feCopy(dst, src *fieldElement) { // // Preconditions: b in {0,1}. func feCSwap(f, g *fieldElement, b int32) { - var x fieldElement b = -b - for i := range x { - x[i] = b & (f[i] ^ g[i]) - } - for i := range f { - f[i] ^= x[i] - } - for i := range g { - g[i] ^= x[i] + t := b & (f[i] ^ g[i]) + f[i] ^= t + g[i] ^= t } } @@ -75,12 +73,7 @@ func load3(in []byte) int64 { // load4 reads a 32-bit, little-endian value from in. func load4(in []byte) int64 { - var r int64 - r = int64(in[0]) - r |= int64(in[1]) << 8 - r |= int64(in[2]) << 16 - r |= int64(in[3]) << 24 - return r + return int64(binary.LittleEndian.Uint32(in)) } func feFromBytes(dst *fieldElement, src *[32]byte) { diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_test.go b/vendor/golang.org/x/crypto/curve25519/curve25519_test.go index 14b0ee87c..051a8301f 100644 --- a/vendor/golang.org/x/crypto/curve25519/curve25519_test.go +++ b/vendor/golang.org/x/crypto/curve25519/curve25519_test.go @@ -27,3 +27,13 @@ func TestBaseScalarMult(t *testing.T) { t.Errorf("incorrect result: got %s, want %s", result, expectedHex) } } + +func BenchmarkScalarBaseMult(b *testing.B) { + var in, out [32]byte + in[0] = 1 + + b.SetBytes(32) + for i := 0; i < b.N; i++ { + ScalarBaseMult(&out, &in) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/agent/client_test.go b/vendor/golang.org/x/crypto/ssh/agent/client_test.go index a13a65001..93d3a9cd2 100644 --- a/vendor/golang.org/x/crypto/ssh/agent/client_test.go +++ b/vendor/golang.org/x/crypto/ssh/agent/client_test.go @@ -233,7 +233,9 @@ func TestAuth(t *testing.T) { conn.Close() }() - conf := ssh.ClientConfig{} + conf := ssh.ClientConfig{ + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } conf.Auth = append(conf.Auth, ssh.PublicKeysCallback(agent.Signers)) conn, _, _, err := ssh.NewClientConn(b, "", &conf) if err != nil { diff --git a/vendor/golang.org/x/crypto/ssh/agent/example_test.go b/vendor/golang.org/x/crypto/ssh/agent/example_test.go index c1130f77a..85562253e 100644 --- a/vendor/golang.org/x/crypto/ssh/agent/example_test.go +++ b/vendor/golang.org/x/crypto/ssh/agent/example_test.go @@ -6,20 +6,20 @@ package agent_test import ( "log" - "os" "net" + "os" - "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/agent" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" ) func ExampleClientAgent() { // ssh-agent has a UNIX socket under $SSH_AUTH_SOCK socket := os.Getenv("SSH_AUTH_SOCK") - conn, err := net.Dial("unix", socket) - if err != nil { - log.Fatalf("net.Dial: %v", err) - } + conn, err := net.Dial("unix", socket) + if err != nil { + log.Fatalf("net.Dial: %v", err) + } agentClient := agent.NewClient(conn) config := &ssh.ClientConfig{ User: "username", @@ -29,6 +29,7 @@ func ExampleClientAgent() { // wants it. ssh.PublicKeysCallback(agentClient.Signers), }, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), } sshc, err := ssh.Dial("tcp", "localhost:22", config) diff --git a/vendor/golang.org/x/crypto/ssh/agent/server_test.go b/vendor/golang.org/x/crypto/ssh/agent/server_test.go index ec9cdeeb5..6b0837d94 100644 --- a/vendor/golang.org/x/crypto/ssh/agent/server_test.go +++ b/vendor/golang.org/x/crypto/ssh/agent/server_test.go @@ -56,7 +56,9 @@ func TestSetupForwardAgent(t *testing.T) { incoming <- conn }() - conf := ssh.ClientConfig{} + conf := ssh.ClientConfig{ + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } conn, chans, reqs, err := ssh.NewClientConn(b, "", &conf) if err != nil { t.Fatalf("NewClientConn: %v", err) diff --git a/vendor/golang.org/x/crypto/ssh/certs.go b/vendor/golang.org/x/crypto/ssh/certs.go index 6331c94d5..67600e240 100644 --- a/vendor/golang.org/x/crypto/ssh/certs.go +++ b/vendor/golang.org/x/crypto/ssh/certs.go @@ -268,7 +268,7 @@ type CertChecker struct { // HostKeyFallback is called when CertChecker.CheckHostKey encounters a // public key that is not a certificate. It must implement host key // validation or else, if nil, all such keys are rejected. - HostKeyFallback func(addr string, remote net.Addr, key PublicKey) error + HostKeyFallback HostKeyCallback // IsRevoked is called for each certificate so that revocation checking // can be implemented. It should return true if the given certificate diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go index c97f2978e..a7e3263bc 100644 --- a/vendor/golang.org/x/crypto/ssh/client.go +++ b/vendor/golang.org/x/crypto/ssh/client.go @@ -5,6 +5,7 @@ package ssh import ( + "bytes" "errors" "fmt" "net" @@ -13,7 +14,7 @@ import ( ) // Client implements a traditional SSH client that supports shells, -// subprocesses, port forwarding and tunneled dialing. +// subprocesses, TCP port/streamlocal forwarding and tunneled dialing. type Client struct { Conn @@ -59,6 +60,7 @@ func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client { conn.forwards.closeAll() }() go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip")) + go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-streamlocal@openssh.com")) return conn } @@ -68,6 +70,11 @@ func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client { func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) { fullConf := *config fullConf.SetDefaults() + if fullConf.HostKeyCallback == nil { + c.Close() + return nil, nil, nil, errors.New("ssh: must specify HostKeyCallback") + } + conn := &connection{ sshConn: sshConn{conn: c}, } @@ -173,6 +180,13 @@ func Dial(network, addr string, config *ClientConfig) (*Client, error) { return NewClient(c, chans, reqs), nil } +// HostKeyCallback is the function type used for verifying server +// keys. A HostKeyCallback must return nil if the host key is OK, or +// an error to reject it. It receives the hostname as passed to Dial +// or NewClientConn. The remote address is the RemoteAddr of the +// net.Conn underlying the the SSH connection. +type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error + // A ClientConfig structure is used to configure a Client. It must not be // modified after having been passed to an SSH function. type ClientConfig struct { @@ -188,10 +202,12 @@ type ClientConfig struct { // be used during authentication. Auth []AuthMethod - // HostKeyCallback, if not nil, is called during the cryptographic - // handshake to validate the server's host key. A nil HostKeyCallback - // implies that all host keys are accepted. - HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error + // HostKeyCallback is called during the cryptographic + // handshake to validate the server's host key. The client + // configuration must supply this callback for the connection + // to succeed. The functions InsecureIgnoreHostKey or + // FixedHostKey can be used for simplistic host key checks. + HostKeyCallback HostKeyCallback // ClientVersion contains the version identification string that will // be used for the connection. If empty, a reasonable default is used. @@ -209,3 +225,33 @@ type ClientConfig struct { // A Timeout of zero means no timeout. Timeout time.Duration } + +// InsecureIgnoreHostKey returns a function that can be used for +// ClientConfig.HostKeyCallback to accept any host key. It should +// not be used for production code. +func InsecureIgnoreHostKey() HostKeyCallback { + return func(hostname string, remote net.Addr, key PublicKey) error { + return nil + } +} + +type fixedHostKey struct { + key PublicKey +} + +func (f *fixedHostKey) check(hostname string, remote net.Addr, key PublicKey) error { + if f.key == nil { + return fmt.Errorf("ssh: required host key was nil") + } + if !bytes.Equal(key.Marshal(), f.key.Marshal()) { + return fmt.Errorf("ssh: host key mismatch") + } + return nil +} + +// FixedHostKey returns a function for use in +// ClientConfig.HostKeyCallback to accept only a specific host key. +func FixedHostKey(key PublicKey) HostKeyCallback { + hk := &fixedHostKey{key} + return hk.check +} diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go index fd1ec5dda..b882da086 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth.go @@ -179,31 +179,26 @@ func (cb publicKeyCallback) method() string { } func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { - // Authentication is performed in two stages. The first stage sends an - // enquiry to test if each key is acceptable to the remote. The second - // stage attempts to authenticate with the valid keys obtained in the - // first stage. + // Authentication is performed by sending an enquiry to test if a key is + // acceptable to the remote. If the key is acceptable, the client will + // attempt to authenticate with the valid key. If not the client will repeat + // the process with the remaining keys. signers, err := cb() if err != nil { return false, nil, err } - var validKeys []Signer + var methods []string for _, signer := range signers { - if ok, err := validateKey(signer.PublicKey(), user, c); ok { - validKeys = append(validKeys, signer) - } else { - if err != nil { - return false, nil, err - } + ok, err := validateKey(signer.PublicKey(), user, c) + if err != nil { + return false, nil, err + } + if !ok { + continue } - } - // methods that may continue if this auth is not successful. - var methods []string - for _, signer := range validKeys { pub := signer.PublicKey() - pubKey := pub.Marshal() sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{ User: user, @@ -236,13 +231,29 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand if err != nil { return false, nil, err } - if success { + + // If authentication succeeds or the list of available methods does not + // contain the "publickey" method, do not attempt to authenticate with any + // other keys. According to RFC 4252 Section 7, the latter can occur when + // additional authentication methods are required. + if success || !containsMethod(methods, cb.method()) { return success, methods, err } } + return false, methods, nil } +func containsMethod(methods []string, method string) bool { + for _, m := range methods { + if m == method { + return true + } + } + + return false +} + // validateKey validates the key provided is acceptable to the server. func validateKey(key PublicKey, user string, c packetConn) (bool, error) { pubKey := key.Marshal() diff --git a/vendor/golang.org/x/crypto/ssh/client_auth_test.go b/vendor/golang.org/x/crypto/ssh/client_auth_test.go index e384c796b..dd83a3c84 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth_test.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth_test.go @@ -76,8 +76,6 @@ func tryAuth(t *testing.T, config *ClientConfig) error { } return nil, errors.New("keyboard-interactive failed") }, - AuthLogCallback: func(conn ConnMetadata, method string, err error) { - }, } serverConfig.AddHostKey(testSigners["rsa"]) @@ -92,6 +90,7 @@ func TestClientAuthPublicKey(t *testing.T) { Auth: []AuthMethod{ PublicKeys(testSigners["rsa"]), }, + HostKeyCallback: InsecureIgnoreHostKey(), } if err := tryAuth(t, config); err != nil { t.Fatalf("unable to dial remote side: %s", err) @@ -104,6 +103,7 @@ func TestAuthMethodPassword(t *testing.T) { Auth: []AuthMethod{ Password(clientPassword), }, + HostKeyCallback: InsecureIgnoreHostKey(), } if err := tryAuth(t, config); err != nil { @@ -123,6 +123,7 @@ func TestAuthMethodFallback(t *testing.T) { return "WRONG", nil }), }, + HostKeyCallback: InsecureIgnoreHostKey(), } if err := tryAuth(t, config); err != nil { @@ -141,6 +142,7 @@ func TestAuthMethodWrongPassword(t *testing.T) { Password("wrong"), PublicKeys(testSigners["rsa"]), }, + HostKeyCallback: InsecureIgnoreHostKey(), } if err := tryAuth(t, config); err != nil { @@ -158,6 +160,7 @@ func TestAuthMethodKeyboardInteractive(t *testing.T) { Auth: []AuthMethod{ KeyboardInteractive(answers.Challenge), }, + HostKeyCallback: InsecureIgnoreHostKey(), } if err := tryAuth(t, config); err != nil { @@ -203,6 +206,7 @@ func TestAuthMethodRSAandDSA(t *testing.T) { Auth: []AuthMethod{ PublicKeys(testSigners["dsa"], testSigners["rsa"]), }, + HostKeyCallback: InsecureIgnoreHostKey(), } if err := tryAuth(t, config); err != nil { t.Fatalf("client could not authenticate with rsa key: %v", err) @@ -219,6 +223,7 @@ func TestClientHMAC(t *testing.T) { Config: Config{ MACs: []string{mac}, }, + HostKeyCallback: InsecureIgnoreHostKey(), } if err := tryAuth(t, config); err != nil { t.Fatalf("client could not authenticate with mac algo %s: %v", mac, err) @@ -254,6 +259,7 @@ func TestClientUnsupportedKex(t *testing.T) { Config: Config{ KeyExchanges: []string{"diffie-hellman-group-exchange-sha256"}, // not currently supported }, + HostKeyCallback: InsecureIgnoreHostKey(), } if err := tryAuth(t, config); err == nil || !strings.Contains(err.Error(), "common algorithm") { t.Errorf("got %v, expected 'common algorithm'", err) @@ -273,7 +279,8 @@ func TestClientLoginCert(t *testing.T) { } clientConfig := &ClientConfig{ - User: "user", + User: "user", + HostKeyCallback: InsecureIgnoreHostKey(), } clientConfig.Auth = append(clientConfig.Auth, PublicKeys(certSigner)) @@ -363,6 +370,7 @@ func testPermissionsPassing(withPermissions bool, t *testing.T) { Auth: []AuthMethod{ PublicKeys(testSigners["rsa"]), }, + HostKeyCallback: InsecureIgnoreHostKey(), } if withPermissions { clientConfig.User = "permissions" @@ -409,6 +417,7 @@ func TestRetryableAuth(t *testing.T) { }), 2), PublicKeys(testSigners["rsa"]), }, + HostKeyCallback: InsecureIgnoreHostKey(), } if err := tryAuth(t, config); err != nil { @@ -430,7 +439,8 @@ func ExampleRetryableAuthMethod(t *testing.T) { } config := &ClientConfig{ - User: user, + HostKeyCallback: InsecureIgnoreHostKey(), + User: user, Auth: []AuthMethod{ RetryableAuthMethod(KeyboardInteractiveChallenge(Cb), NumberOfPrompts), }, @@ -450,7 +460,8 @@ func TestClientAuthNone(t *testing.T) { serverConfig.AddHostKey(testSigners["rsa"]) clientConfig := &ClientConfig{ - User: user, + User: user, + HostKeyCallback: InsecureIgnoreHostKey(), } c1, c2, err := netPipe() @@ -469,3 +480,100 @@ func TestClientAuthNone(t *testing.T) { t.Fatalf("server: got %q, want %q", serverConn.User(), user) } } + +// Test if authentication attempts are limited on server when MaxAuthTries is set +func TestClientAuthMaxAuthTries(t *testing.T) { + user := "testuser" + + serverConfig := &ServerConfig{ + MaxAuthTries: 2, + PasswordCallback: func(conn ConnMetadata, pass []byte) (*Permissions, error) { + if conn.User() == "testuser" && string(pass) == "right" { + return nil, nil + } + return nil, errors.New("password auth failed") + }, + } + serverConfig.AddHostKey(testSigners["rsa"]) + + expectedErr := fmt.Errorf("ssh: handshake failed: %v", &disconnectMsg{ + Reason: 2, + Message: "too many authentication failures", + }) + + for tries := 2; tries < 4; tries++ { + n := tries + clientConfig := &ClientConfig{ + User: user, + Auth: []AuthMethod{ + RetryableAuthMethod(PasswordCallback(func() (string, error) { + n-- + if n == 0 { + return "right", nil + } else { + return "wrong", nil + } + }), tries), + }, + HostKeyCallback: InsecureIgnoreHostKey(), + } + + c1, c2, err := netPipe() + if err != nil { + t.Fatalf("netPipe: %v", err) + } + defer c1.Close() + defer c2.Close() + + go newServer(c1, serverConfig) + _, _, _, err = NewClientConn(c2, "", clientConfig) + if tries > 2 { + if err == nil { + t.Fatalf("client: got no error, want %s", expectedErr) + } else if err.Error() != expectedErr.Error() { + t.Fatalf("client: got %s, want %s", err, expectedErr) + } + } else { + if err != nil { + t.Fatalf("client: got %s, want no error", err) + } + } + } +} + +// Test if authentication attempts are correctly limited on server +// when more public keys are provided then MaxAuthTries +func TestClientAuthMaxAuthTriesPublicKey(t *testing.T) { + signers := []Signer{} + for i := 0; i < 6; i++ { + signers = append(signers, testSigners["dsa"]) + } + + validConfig := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + PublicKeys(append([]Signer{testSigners["rsa"]}, signers...)...), + }, + HostKeyCallback: InsecureIgnoreHostKey(), + } + if err := tryAuth(t, validConfig); err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } + + expectedErr := fmt.Errorf("ssh: handshake failed: %v", &disconnectMsg{ + Reason: 2, + Message: "too many authentication failures", + }) + invalidConfig := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + PublicKeys(append(signers, testSigners["rsa"])...), + }, + HostKeyCallback: InsecureIgnoreHostKey(), + } + if err := tryAuth(t, invalidConfig); err == nil { + t.Fatalf("client: got no error, want %s", expectedErr) + } else if err.Error() != expectedErr.Error() { + t.Fatalf("client: got %s, want %s", err, expectedErr) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/client_test.go b/vendor/golang.org/x/crypto/ssh/client_test.go index 1fe790cb4..ccf56074d 100644 --- a/vendor/golang.org/x/crypto/ssh/client_test.go +++ b/vendor/golang.org/x/crypto/ssh/client_test.go @@ -6,6 +6,7 @@ package ssh import ( "net" + "strings" "testing" ) @@ -13,6 +14,7 @@ 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) if err != nil { @@ -37,3 +39,43 @@ func TestCustomClientVersion(t *testing.T) { func TestDefaultClientVersion(t *testing.T) { testClientVersion(t, &ClientConfig{}, packageVersion) } + +func TestHostKeyCheck(t *testing.T) { + for _, tt := range []struct { + name string + wantError string + key PublicKey + }{ + {"no callback", "must specify HostKeyCallback", nil}, + {"correct key", "", testSigners["rsa"].PublicKey()}, + {"mismatch", "mismatch", testSigners["ecdsa"].PublicKey()}, + } { + c1, c2, err := netPipe() + if err != nil { + t.Fatalf("netPipe: %v", err) + } + defer c1.Close() + defer c2.Close() + serverConf := &ServerConfig{ + NoClientAuth: true, + } + serverConf.AddHostKey(testSigners["rsa"]) + + go NewServerConn(c1, serverConf) + clientConf := ClientConfig{ + User: "user", + } + if tt.key != nil { + clientConf.HostKeyCallback = FixedHostKey(tt.key) + } + + _, _, _, err = NewClientConn(c2, "", &clientConf) + if err != nil { + if tt.wantError == "" || !strings.Contains(err.Error(), tt.wantError) { + t.Errorf("%s: got error %q, missing %q", tt.name, err.Error(), tt.wantError) + } + } else if tt.wantError != "" { + t.Errorf("%s: succeeded, but want error string %q", tt.name, tt.wantError) + } + } +} diff --git a/vendor/golang.org/x/crypto/ssh/common.go b/vendor/golang.org/x/crypto/ssh/common.go index 8656d0f85..dc39e4d23 100644 --- a/vendor/golang.org/x/crypto/ssh/common.go +++ b/vendor/golang.org/x/crypto/ssh/common.go @@ -9,6 +9,7 @@ import ( "crypto/rand" "fmt" "io" + "math" "sync" _ "crypto/sha1" @@ -40,7 +41,7 @@ var supportedKexAlgos = []string{ kexAlgoDH14SHA1, kexAlgoDH1SHA1, } -// supportedKexAlgos specifies the supported host-key algorithms (i.e. methods +// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods // of authenticating servers) in preference order. var supportedHostKeyAlgos = []string{ CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, @@ -186,7 +187,7 @@ type Config struct { // The maximum number of bytes sent or received after which a // new key is negotiated. It must be at least 256. If - // unspecified, 1 gigabyte is used. + // unspecified, a size suitable for the chosen cipher is used. RekeyThreshold uint64 // The allowed key exchanges algorithms. If unspecified then a @@ -230,11 +231,12 @@ func (c *Config) SetDefaults() { } if c.RekeyThreshold == 0 { - // RFC 4253, section 9 suggests rekeying after 1G. - c.RekeyThreshold = 1 << 30 - } - if c.RekeyThreshold < minRekeyThreshold { + // cipher specific default + } else if c.RekeyThreshold < minRekeyThreshold { c.RekeyThreshold = minRekeyThreshold + } else if c.RekeyThreshold >= math.MaxInt64 { + // Avoid weirdness if somebody uses -1 as a threshold. + c.RekeyThreshold = math.MaxInt64 } } diff --git a/vendor/golang.org/x/crypto/ssh/doc.go b/vendor/golang.org/x/crypto/ssh/doc.go index d6be89466..67b7322c0 100644 --- a/vendor/golang.org/x/crypto/ssh/doc.go +++ b/vendor/golang.org/x/crypto/ssh/doc.go @@ -14,5 +14,8 @@ others. References: [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 + +This package does not fall under the stability promise of the Go language itself, +so its API may be changed when pressing needs arise. */ package ssh // import "golang.org/x/crypto/ssh" diff --git a/vendor/golang.org/x/crypto/ssh/example_test.go b/vendor/golang.org/x/crypto/ssh/example_test.go index 4d2eabd0f..618398cea 100644 --- a/vendor/golang.org/x/crypto/ssh/example_test.go +++ b/vendor/golang.org/x/crypto/ssh/example_test.go @@ -5,12 +5,16 @@ package ssh_test import ( + "bufio" "bytes" "fmt" "io/ioutil" "log" "net" "net/http" + "os" + "path/filepath" + "strings" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/terminal" @@ -91,8 +95,6 @@ func ExampleNewServerConn() { go ssh.DiscardRequests(reqs) // Service the incoming Channel channel. - - // Service the incoming Channel channel. for newChannel := range chans { // Channels have a type, depending on the application level // protocol intended. In the case of a shell, the type is @@ -131,16 +133,59 @@ func ExampleNewServerConn() { } } +func ExampleHostKeyCheck() { + // Every client must provide a host key check. Here is a + // simple-minded parse of OpenSSH's known_hosts file + host := "hostname" + file, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")) + if err != nil { + log.Fatal(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + var hostKey ssh.PublicKey + for scanner.Scan() { + fields := strings.Split(scanner.Text(), " ") + if len(fields) != 3 { + continue + } + if strings.Contains(fields[0], host) { + var err error + hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes()) + if err != nil { + log.Fatalf("error parsing %q: %v", fields[2], err) + } + break + } + } + + if hostKey == nil { + log.Fatalf("no hostkey for %s", host) + } + + config := ssh.ClientConfig{ + User: os.Getenv("USER"), + HostKeyCallback: ssh.FixedHostKey(hostKey), + } + + _, err = ssh.Dial("tcp", host+":22", &config) + log.Println(err) +} + func ExampleDial() { + var hostKey ssh.PublicKey // An SSH client is represented with a ClientConn. // // To authenticate with the remote server you must pass at least one - // implementation of AuthMethod via the Auth field in ClientConfig. + // implementation of AuthMethod via the Auth field in ClientConfig, + // and provide a HostKeyCallback. config := &ssh.ClientConfig{ User: "username", Auth: []ssh.AuthMethod{ ssh.Password("yourpassword"), }, + HostKeyCallback: ssh.FixedHostKey(hostKey), } client, err := ssh.Dial("tcp", "yourserver.com:22", config) if err != nil { @@ -166,6 +211,7 @@ func ExampleDial() { } func ExamplePublicKeys() { + var hostKey ssh.PublicKey // A public key may be used to authenticate against the remote // server by using an unencrypted PEM-encoded private key file. // @@ -188,6 +234,7 @@ func ExamplePublicKeys() { // Use the PublicKeys method for remote authentication. ssh.PublicKeys(signer), }, + HostKeyCallback: ssh.FixedHostKey(hostKey), } // Connect to the remote server and perform the SSH handshake. @@ -199,11 +246,13 @@ func ExamplePublicKeys() { } func ExampleClient_Listen() { + var hostKey ssh.PublicKey config := &ssh.ClientConfig{ User: "username", Auth: []ssh.AuthMethod{ ssh.Password("password"), }, + HostKeyCallback: ssh.FixedHostKey(hostKey), } // Dial your ssh server. conn, err := ssh.Dial("tcp", "localhost:22", config) @@ -226,12 +275,14 @@ func ExampleClient_Listen() { } func ExampleSession_RequestPty() { + var hostKey ssh.PublicKey // Create client config config := &ssh.ClientConfig{ User: "username", Auth: []ssh.AuthMethod{ ssh.Password("password"), }, + HostKeyCallback: ssh.FixedHostKey(hostKey), } // Connect to ssh server conn, err := ssh.Dial("tcp", "localhost:22", config) diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go index 8de650644..932ce8393 100644 --- a/vendor/golang.org/x/crypto/ssh/handshake.go +++ b/vendor/golang.org/x/crypto/ssh/handshake.go @@ -74,7 +74,7 @@ type handshakeTransport struct { startKex chan *pendingKex // data for host key checking - hostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error + hostKeyCallback HostKeyCallback dialAddress string remoteAddr net.Addr @@ -107,6 +107,8 @@ func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, config: config, } + t.resetReadThresholds() + t.resetWriteThresholds() // We always start with a mandatory key exchange. t.requestKex <- struct{}{} @@ -237,6 +239,17 @@ func (t *handshakeTransport) requestKeyExchange() { } } +func (t *handshakeTransport) resetWriteThresholds() { + t.writePacketsLeft = packetRekeyThreshold + if t.config.RekeyThreshold > 0 { + t.writeBytesLeft = int64(t.config.RekeyThreshold) + } else if t.algorithms != nil { + t.writeBytesLeft = t.algorithms.w.rekeyBytes() + } else { + t.writeBytesLeft = 1 << 30 + } +} + func (t *handshakeTransport) kexLoop() { write: @@ -285,12 +298,8 @@ write: t.writeError = err t.sentInitPacket = nil t.sentInitMsg = nil - t.writePacketsLeft = packetRekeyThreshold - if t.config.RekeyThreshold > 0 { - t.writeBytesLeft = int64(t.config.RekeyThreshold) - } else if t.algorithms != nil { - t.writeBytesLeft = t.algorithms.w.rekeyBytes() - } + + t.resetWriteThresholds() // we have completed the key exchange. Since the // reader is still blocked, it is safe to clear out @@ -344,6 +353,17 @@ write: // key exchange itself. const packetRekeyThreshold = (1 << 31) +func (t *handshakeTransport) resetReadThresholds() { + t.readPacketsLeft = packetRekeyThreshold + if t.config.RekeyThreshold > 0 { + t.readBytesLeft = int64(t.config.RekeyThreshold) + } else if t.algorithms != nil { + t.readBytesLeft = t.algorithms.r.rekeyBytes() + } else { + t.readBytesLeft = 1 << 30 + } +} + func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) { p, err := t.conn.readPacket() if err != nil { @@ -391,12 +411,7 @@ func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) { return nil, err } - t.readPacketsLeft = packetRekeyThreshold - if t.config.RekeyThreshold > 0 { - t.readBytesLeft = int64(t.config.RekeyThreshold) - } else { - t.readBytesLeft = t.algorithms.r.rekeyBytes() - } + t.resetReadThresholds() // By default, a key exchange is hidden from higher layers by // translating it into msgIgnore. @@ -574,7 +589,9 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { } result.SessionID = t.sessionID - t.conn.prepareKeyChange(t.algorithms, result) + if err := t.conn.prepareKeyChange(t.algorithms, result); err != nil { + return err + } if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { return err } @@ -614,11 +631,9 @@ func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics * return nil, err } - if t.hostKeyCallback != nil { - err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey) - if err != nil { - return nil, err - } + err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey) + if err != nil { + return nil, err } return result, nil diff --git a/vendor/golang.org/x/crypto/ssh/handshake_test.go b/vendor/golang.org/x/crypto/ssh/handshake_test.go index 1b831127e..51a4c5ade 100644 --- a/vendor/golang.org/x/crypto/ssh/handshake_test.go +++ b/vendor/golang.org/x/crypto/ssh/handshake_test.go @@ -436,6 +436,7 @@ func testHandshakeErrorHandlingN(t *testing.T, readLimit, writeLimit int, couple clientConf.SetDefaults() clientConn := newHandshakeTransport(&errorKeyingTransport{b, -1, -1}, &clientConf, []byte{'a'}, []byte{'b'}) clientConn.hostKeyAlgorithms = []string{key.PublicKey().Type()} + clientConn.hostKeyCallback = InsecureIgnoreHostKey() go clientConn.readLoop() go clientConn.kexLoop() @@ -525,3 +526,31 @@ func TestDisconnect(t *testing.T) { t.Errorf("readPacket 3 succeeded") } } + +func TestHandshakeRekeyDefault(t *testing.T) { + clientConf := &ClientConfig{ + Config: Config{ + Ciphers: []string{"aes128-ctr"}, + }, + HostKeyCallback: InsecureIgnoreHostKey(), + } + trC, trS, err := handshakePair(clientConf, "addr", false) + if err != nil { + t.Fatalf("handshakePair: %v", err) + } + defer trC.Close() + defer trS.Close() + + trC.writePacket([]byte{msgRequestSuccess, 0, 0}) + trC.Close() + + rgb := (1024 + trC.readBytesLeft) >> 30 + wgb := (1024 + trC.writeBytesLeft) >> 30 + + if rgb != 64 { + t.Errorf("got rekey after %dG read, want 64G", rgb) + } + if wgb != 64 { + t.Errorf("got rekey after %dG write, want 64G", wgb) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go index f38de9898..cf6853232 100644 --- a/vendor/golang.org/x/crypto/ssh/keys.go +++ b/vendor/golang.org/x/crypto/ssh/keys.go @@ -824,7 +824,7 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { // Implemented based on the documentation at // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key -func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) { +func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) { magic := append([]byte("openssh-key-v1"), 0) if !bytes.Equal(magic, key[0:len(magic)]) { return nil, errors.New("ssh: invalid openssh private key format") @@ -844,14 +844,15 @@ func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) { return nil, err } + if w.KdfName != "none" || w.CipherName != "none" { + return nil, errors.New("ssh: cannot decode encrypted private keys") + } + pk1 := struct { Check1 uint32 Check2 uint32 Keytype string - Pub []byte - Priv []byte - Comment string - Pad []byte `ssh:"rest"` + Rest []byte `ssh:"rest"` }{} if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil { @@ -862,24 +863,75 @@ func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) { return nil, errors.New("ssh: checkint mismatch") } - // we only handle ed25519 keys currently - if pk1.Keytype != KeyAlgoED25519 { - return nil, errors.New("ssh: unhandled key type") - } + // we only handle ed25519 and rsa keys currently + switch pk1.Keytype { + case KeyAlgoRSA: + // https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L2760-L2773 + key := struct { + N *big.Int + E *big.Int + D *big.Int + Iqmp *big.Int + P *big.Int + Q *big.Int + Comment string + Pad []byte `ssh:"rest"` + }{} + + if err := Unmarshal(pk1.Rest, &key); err != nil { + return nil, err + } - for i, b := range pk1.Pad { - if int(b) != i+1 { - return nil, errors.New("ssh: padding not as expected") + for i, b := range key.Pad { + if int(b) != i+1 { + return nil, errors.New("ssh: padding not as expected") + } } - } - if len(pk1.Priv) != ed25519.PrivateKeySize { - return nil, errors.New("ssh: private key unexpected length") - } + pk := &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: key.N, + E: int(key.E.Int64()), + }, + D: key.D, + Primes: []*big.Int{key.P, key.Q}, + } - pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize)) - copy(pk, pk1.Priv) - return &pk, nil + if err := pk.Validate(); err != nil { + return nil, err + } + + pk.Precompute() + + return pk, nil + case KeyAlgoED25519: + key := struct { + Pub []byte + Priv []byte + Comment string + Pad []byte `ssh:"rest"` + }{} + + if err := Unmarshal(pk1.Rest, &key); err != nil { + return nil, err + } + + if len(key.Priv) != ed25519.PrivateKeySize { + return nil, errors.New("ssh: private key unexpected length") + } + + for i, b := range key.Pad { + if int(b) != i+1 { + return nil, errors.New("ssh: padding not as expected") + } + } + + pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize)) + copy(pk, key.Priv) + return &pk, nil + default: + return nil, errors.New("ssh: unhandled key type") + } } // FingerprintLegacyMD5 returns the user presentation of the key's diff --git a/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go b/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go new file mode 100644 index 000000000..d1f371868 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go @@ -0,0 +1,547 @@ +// Copyright 2017 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 knownhosts implements a parser for the OpenSSH +// known_hosts host key database. +package knownhosts + +import ( + "bufio" + "bytes" + "crypto/hmac" + "crypto/rand" + "crypto/sha1" + "encoding/base64" + "errors" + "fmt" + "io" + "net" + "os" + "strings" + + "golang.org/x/crypto/ssh" +) + +// See the sshd manpage +// (http://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT) for +// background. + +type addr struct{ host, port string } + +func (a *addr) String() string { + h := a.host + if strings.Contains(h, ":") { + h = "[" + h + "]" + } + return h + ":" + a.port +} + +type matcher interface { + match([]addr) bool +} + +type hostPattern struct { + negate bool + addr addr +} + +func (p *hostPattern) String() string { + n := "" + if p.negate { + n = "!" + } + + return n + p.addr.String() +} + +type hostPatterns []hostPattern + +func (ps hostPatterns) match(addrs []addr) bool { + matched := false + for _, p := range ps { + for _, a := range addrs { + m := p.match(a) + if !m { + continue + } + if p.negate { + return false + } + matched = true + } + } + return matched +} + +// See +// https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/addrmatch.c +// The matching of * has no regard for separators, unlike filesystem globs +func wildcardMatch(pat []byte, str []byte) bool { + for { + if len(pat) == 0 { + return len(str) == 0 + } + if len(str) == 0 { + return false + } + + if pat[0] == '*' { + if len(pat) == 1 { + return true + } + + for j := range str { + if wildcardMatch(pat[1:], str[j:]) { + return true + } + } + return false + } + + if pat[0] == '?' || pat[0] == str[0] { + pat = pat[1:] + str = str[1:] + } else { + return false + } + } +} + +func (l *hostPattern) match(a addr) bool { + return wildcardMatch([]byte(l.addr.host), []byte(a.host)) && l.addr.port == a.port +} + +type keyDBLine struct { + cert bool + matcher matcher + knownKey KnownKey +} + +func serialize(k ssh.PublicKey) string { + return k.Type() + " " + base64.StdEncoding.EncodeToString(k.Marshal()) +} + +func (l *keyDBLine) match(addrs []addr) bool { + return l.matcher.match(addrs) +} + +type hostKeyDB struct { + // Serialized version of revoked keys + revoked map[string]*KnownKey + lines []keyDBLine +} + +func newHostKeyDB() *hostKeyDB { + db := &hostKeyDB{ + revoked: make(map[string]*KnownKey), + } + + return db +} + +func keyEq(a, b ssh.PublicKey) bool { + return bytes.Equal(a.Marshal(), b.Marshal()) +} + +// IsAuthority can be used as a callback in ssh.CertChecker +func (db *hostKeyDB) IsAuthority(remote ssh.PublicKey) bool { + for _, l := range db.lines { + // TODO(hanwen): should we check the hostname against host pattern? + if l.cert && keyEq(l.knownKey.Key, remote) { + return true + } + } + return false +} + +// IsRevoked can be used as a callback in ssh.CertChecker +func (db *hostKeyDB) IsRevoked(key *ssh.Certificate) bool { + _, ok := db.revoked[string(key.Marshal())] + return ok +} + +const markerCert = "@cert-authority" +const markerRevoked = "@revoked" + +func nextWord(line []byte) (string, []byte) { + i := bytes.IndexAny(line, "\t ") + if i == -1 { + return string(line), nil + } + + return string(line[:i]), bytes.TrimSpace(line[i:]) +} + +func parseLine(line []byte) (marker, host string, key ssh.PublicKey, err error) { + if w, next := nextWord(line); w == markerCert || w == markerRevoked { + marker = w + line = next + } + + host, line = nextWord(line) + if len(line) == 0 { + return "", "", nil, errors.New("knownhosts: missing host pattern") + } + + // ignore the keytype as it's in the key blob anyway. + _, line = nextWord(line) + if len(line) == 0 { + return "", "", nil, errors.New("knownhosts: missing key type pattern") + } + + keyBlob, _ := nextWord(line) + + keyBytes, err := base64.StdEncoding.DecodeString(keyBlob) + if err != nil { + return "", "", nil, err + } + key, err = ssh.ParsePublicKey(keyBytes) + if err != nil { + return "", "", nil, err + } + + return marker, host, key, nil +} + +func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error { + marker, pattern, key, err := parseLine(line) + if err != nil { + return err + } + + if marker == markerRevoked { + db.revoked[string(key.Marshal())] = &KnownKey{ + Key: key, + Filename: filename, + Line: linenum, + } + + return nil + } + + entry := keyDBLine{ + cert: marker == markerCert, + knownKey: KnownKey{ + Filename: filename, + Line: linenum, + Key: key, + }, + } + + if pattern[0] == '|' { + entry.matcher, err = newHashedHost(pattern) + } else { + entry.matcher, err = newHostnameMatcher(pattern) + } + + if err != nil { + return err + } + + db.lines = append(db.lines, entry) + return nil +} + +func newHostnameMatcher(pattern string) (matcher, error) { + var hps hostPatterns + for _, p := range strings.Split(pattern, ",") { + if len(p) == 0 { + continue + } + + var a addr + var negate bool + if p[0] == '!' { + negate = true + p = p[1:] + } + + if len(p) == 0 { + return nil, errors.New("knownhosts: negation without following hostname") + } + + var err error + if p[0] == '[' { + a.host, a.port, err = net.SplitHostPort(p) + if err != nil { + return nil, err + } + } else { + a.host, a.port, err = net.SplitHostPort(p) + if err != nil { + a.host = p + a.port = "22" + } + } + hps = append(hps, hostPattern{ + negate: negate, + addr: a, + }) + } + return hps, nil +} + +// KnownKey represents a key declared in a known_hosts file. +type KnownKey struct { + Key ssh.PublicKey + Filename string + Line int +} + +func (k *KnownKey) String() string { + return fmt.Sprintf("%s:%d: %s", k.Filename, k.Line, serialize(k.Key)) +} + +// KeyError is returned if we did not find the key in the host key +// database, or there was a mismatch. Typically, in batch +// applications, this should be interpreted as failure. Interactive +// applications can offer an interactive prompt to the user. +type KeyError struct { + // Want holds the accepted host keys. For each key algorithm, + // there can be one hostkey. If Want is empty, the host is + // unknown. If Want is non-empty, there was a mismatch, which + // can signify a MITM attack. + Want []KnownKey +} + +func (u *KeyError) Error() string { + if len(u.Want) == 0 { + return "knownhosts: key is unknown" + } + return "knownhosts: key mismatch" +} + +// RevokedError is returned if we found a key that was revoked. +type RevokedError struct { + Revoked KnownKey +} + +func (r *RevokedError) Error() string { + return "knownhosts: key is revoked" +} + +// check checks a key against the host database. This should not be +// used for verifying certificates. +func (db *hostKeyDB) check(address string, remote net.Addr, remoteKey ssh.PublicKey) error { + if revoked := db.revoked[string(remoteKey.Marshal())]; revoked != nil { + return &RevokedError{Revoked: *revoked} + } + + host, port, err := net.SplitHostPort(remote.String()) + if err != nil { + return fmt.Errorf("knownhosts: SplitHostPort(%s): %v", remote, err) + } + + addrs := []addr{ + {host, port}, + } + + if address != "" { + host, port, err := net.SplitHostPort(address) + if err != nil { + return fmt.Errorf("knownhosts: SplitHostPort(%s): %v", address, err) + } + + addrs = append(addrs, addr{host, port}) + } + + return db.checkAddrs(addrs, remoteKey) +} + +// checkAddrs checks if we can find the given public key for any of +// the given addresses. If we only find an entry for the IP address, +// or only the hostname, then this still succeeds. +func (db *hostKeyDB) checkAddrs(addrs []addr, remoteKey ssh.PublicKey) error { + // TODO(hanwen): are these the right semantics? What if there + // is just a key for the IP address, but not for the + // hostname? + + // Algorithm => key. + knownKeys := map[string]KnownKey{} + for _, l := range db.lines { + if l.match(addrs) { + typ := l.knownKey.Key.Type() + if _, ok := knownKeys[typ]; !ok { + knownKeys[typ] = l.knownKey + } + } + } + + keyErr := &KeyError{} + for _, v := range knownKeys { + keyErr.Want = append(keyErr.Want, v) + } + + // Unknown remote host. + if len(knownKeys) == 0 { + return keyErr + } + + // If the remote host starts using a different, unknown key type, we + // also interpret that as a mismatch. + if known, ok := knownKeys[remoteKey.Type()]; !ok || !keyEq(known.Key, remoteKey) { + return keyErr + } + + return nil +} + +// The Read function parses file contents. +func (db *hostKeyDB) Read(r io.Reader, filename string) error { + scanner := bufio.NewScanner(r) + + lineNum := 0 + for scanner.Scan() { + lineNum++ + line := scanner.Bytes() + line = bytes.TrimSpace(line) + if len(line) == 0 || line[0] == '#' { + continue + } + + if err := db.parseLine(line, filename, lineNum); err != nil { + return fmt.Errorf("knownhosts: %s:%d: %v", filename, lineNum, err) + } + } + return scanner.Err() +} + +// New creates a host key callback from the given OpenSSH host key +// files. The returned callback is for use in +// ssh.ClientConfig.HostKeyCallback. Hostnames are ignored for +// certificates, ie. any certificate authority is assumed to be valid +// for all remote hosts. Hashed hostnames are not supported. +func New(files ...string) (ssh.HostKeyCallback, error) { + db := newHostKeyDB() + for _, fn := range files { + f, err := os.Open(fn) + if err != nil { + return nil, err + } + defer f.Close() + if err := db.Read(f, fn); err != nil { + return nil, err + } + } + + // TODO(hanwen): properly supporting certificates requires an + // API change in the SSH library: IsAuthority should provide + // the address too? + + var certChecker ssh.CertChecker + certChecker.IsAuthority = db.IsAuthority + certChecker.IsRevoked = db.IsRevoked + certChecker.HostKeyFallback = db.check + + return certChecker.CheckHostKey, nil +} + +// Normalize normalizes an address into the form used in known_hosts +func Normalize(address string) string { + host, port, err := net.SplitHostPort(address) + if err != nil { + host = address + port = "22" + } + entry := host + if port != "22" { + entry = "[" + entry + "]:" + port + } else if strings.Contains(host, ":") && !strings.HasPrefix(host, "[") { + entry = "[" + entry + "]" + } + return entry +} + +// Line returns a line to add append to the known_hosts files. +func Line(addresses []string, key ssh.PublicKey) string { + var trimmed []string + for _, a := range addresses { + trimmed = append(trimmed, Normalize(a)) + } + + return strings.Join(trimmed, ",") + " " + serialize(key) +} + +// HashHostname hashes the given hostname. The hostname is not +// normalized before hashing. +func HashHostname(hostname string) string { + // TODO(hanwen): check if we can safely normalize this always. + salt := make([]byte, sha1.Size) + + _, err := rand.Read(salt) + if err != nil { + panic(fmt.Sprintf("crypto/rand failure %v", err)) + } + + hash := hashHost(hostname, salt) + return encodeHash(sha1HashType, salt, hash) +} + +func decodeHash(encoded string) (hashType string, salt, hash []byte, err error) { + if len(encoded) == 0 || encoded[0] != '|' { + err = errors.New("knownhosts: hashed host must start with '|'") + return + } + components := strings.Split(encoded, "|") + if len(components) != 4 { + err = fmt.Errorf("knownhosts: got %d components, want 3", len(components)) + return + } + + hashType = components[1] + if salt, err = base64.StdEncoding.DecodeString(components[2]); err != nil { + return + } + if hash, err = base64.StdEncoding.DecodeString(components[3]); err != nil { + return + } + return +} + +func encodeHash(typ string, salt []byte, hash []byte) string { + return strings.Join([]string{"", + typ, + base64.StdEncoding.EncodeToString(salt), + base64.StdEncoding.EncodeToString(hash), + }, "|") +} + +// See https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/hostfile.c#120 +func hashHost(hostname string, salt []byte) []byte { + mac := hmac.New(sha1.New, salt) + mac.Write([]byte(hostname)) + return mac.Sum(nil) +} + +type hashedHost struct { + salt []byte + hash []byte +} + +const sha1HashType = "1" + +func newHashedHost(encoded string) (*hashedHost, error) { + typ, salt, hash, err := decodeHash(encoded) + if err != nil { + return nil, err + } + + // The type field seems for future algorithm agility, but it's + // actually hardcoded in openssh currently, see + // https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/hostfile.c#120 + if typ != sha1HashType { + return nil, fmt.Errorf("knownhosts: got hash type %s, must be '1'", typ) + } + + return &hashedHost{salt: salt, hash: hash}, nil +} + +func (h *hashedHost) match(addrs []addr) bool { + for _, a := range addrs { + if bytes.Equal(hashHost(Normalize(a.String()), h.salt), h.hash) { + return true + } + } + return false +} diff --git a/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts_test.go b/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts_test.go new file mode 100644 index 000000000..63aff9927 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts_test.go @@ -0,0 +1,307 @@ +// Copyright 2017 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 knownhosts + +import ( + "bytes" + "fmt" + "net" + "reflect" + "testing" + + "golang.org/x/crypto/ssh" +) + +const edKeyStr = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGBAarftlLeoyf+v+nVchEZII/vna2PCV8FaX4vsF5BX" +const alternateEdKeyStr = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIXffBYeYL+WVzVru8npl5JHt2cjlr4ornFTWzoij9sx" +const ecKeyStr = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNLCu01+wpXe3xB5olXCN4SqU2rQu0qjSRKJO4Bg+JRCPU+ENcgdA5srTU8xYDz/GEa4dzK5ldPw4J/gZgSXCMs=" + +var ecKey, alternateEdKey, edKey ssh.PublicKey +var testAddr = &net.TCPAddr{ + IP: net.IP{198, 41, 30, 196}, + Port: 22, +} + +var testAddr6 = &net.TCPAddr{ + IP: net.IP{198, 41, 30, 196, + 1, 2, 3, 4, + 1, 2, 3, 4, + 1, 2, 3, 4, + }, + Port: 22, +} + +func init() { + var err error + ecKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(ecKeyStr)) + if err != nil { + panic(err) + } + edKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(edKeyStr)) + if err != nil { + panic(err) + } + alternateEdKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(alternateEdKeyStr)) + if err != nil { + panic(err) + } +} + +func testDB(t *testing.T, s string) *hostKeyDB { + db := newHostKeyDB() + if err := db.Read(bytes.NewBufferString(s), "testdb"); err != nil { + t.Fatalf("Read: %v", err) + } + + return db +} + +func TestRevoked(t *testing.T) { + db := testDB(t, "\n\n@revoked * "+edKeyStr+"\n") + want := &RevokedError{ + Revoked: KnownKey{ + Key: edKey, + Filename: "testdb", + Line: 3, + }, + } + if err := db.check("", &net.TCPAddr{ + Port: 42, + }, edKey); err == nil { + t.Fatal("no error for revoked key") + } else if !reflect.DeepEqual(want, err) { + t.Fatalf("got %#v, want %#v", want, err) + } +} + +func TestBracket(t *testing.T) { + db := testDB(t, `[git.eclipse.org]:29418,[198.41.30.196]:29418 `+edKeyStr) + + if err := db.check("git.eclipse.org:29418", &net.TCPAddr{ + IP: net.IP{198, 41, 30, 196}, + Port: 29418, + }, edKey); err != nil { + t.Errorf("got error %v, want none", err) + } + + if err := db.check("git.eclipse.org:29419", &net.TCPAddr{ + Port: 42, + }, edKey); err == nil { + t.Fatalf("no error for unknown address") + } else if ke, ok := err.(*KeyError); !ok { + t.Fatalf("got type %T, want *KeyError", err) + } else if len(ke.Want) > 0 { + t.Fatalf("got Want %v, want []", ke.Want) + } +} + +func TestNewKeyType(t *testing.T) { + str := fmt.Sprintf("%s %s", testAddr, edKeyStr) + db := testDB(t, str) + if err := db.check("", testAddr, ecKey); err == nil { + t.Fatalf("no error for unknown address") + } else if ke, ok := err.(*KeyError); !ok { + t.Fatalf("got type %T, want *KeyError", err) + } else if len(ke.Want) == 0 { + t.Fatalf("got empty KeyError.Want") + } +} + +func TestSameKeyType(t *testing.T) { + str := fmt.Sprintf("%s %s", testAddr, edKeyStr) + db := testDB(t, str) + if err := db.check("", testAddr, alternateEdKey); err == nil { + t.Fatalf("no error for unknown address") + } else if ke, ok := err.(*KeyError); !ok { + t.Fatalf("got type %T, want *KeyError", err) + } else if len(ke.Want) == 0 { + t.Fatalf("got empty KeyError.Want") + } else if got, want := ke.Want[0].Key.Marshal(), edKey.Marshal(); !bytes.Equal(got, want) { + t.Fatalf("got key %q, want %q", got, want) + } +} + +func TestIPAddress(t *testing.T) { + str := fmt.Sprintf("%s %s", testAddr, edKeyStr) + db := testDB(t, str) + if err := db.check("", testAddr, edKey); err != nil { + t.Errorf("got error %q, want none", err) + } +} + +func TestIPv6Address(t *testing.T) { + str := fmt.Sprintf("%s %s", testAddr6, edKeyStr) + db := testDB(t, str) + + if err := db.check("", testAddr6, edKey); err != nil { + t.Errorf("got error %q, want none", err) + } +} + +func TestBasic(t *testing.T) { + str := fmt.Sprintf("#comment\n\nserver.org,%s %s\notherhost %s", testAddr, edKeyStr, ecKeyStr) + db := testDB(t, str) + if err := db.check("server.org:22", testAddr, edKey); err != nil { + t.Errorf("got error %q, want none", err) + } + + want := KnownKey{ + Key: edKey, + Filename: "testdb", + Line: 3, + } + if err := db.check("server.org:22", testAddr, ecKey); err == nil { + t.Errorf("succeeded, want KeyError") + } else if ke, ok := err.(*KeyError); !ok { + t.Errorf("got %T, want *KeyError", err) + } else if len(ke.Want) != 1 { + t.Errorf("got %v, want 1 entry", ke) + } else if !reflect.DeepEqual(ke.Want[0], want) { + t.Errorf("got %v, want %v", ke.Want[0], want) + } +} + +func TestNegate(t *testing.T) { + str := fmt.Sprintf("%s,!server.org %s", testAddr, edKeyStr) + db := testDB(t, str) + if err := db.check("server.org:22", testAddr, ecKey); err == nil { + t.Errorf("succeeded") + } else if ke, ok := err.(*KeyError); !ok { + t.Errorf("got error type %T, want *KeyError", err) + } else if len(ke.Want) != 0 { + t.Errorf("got expected keys %d (first of type %s), want []", len(ke.Want), ke.Want[0].Key.Type()) + } +} + +func TestWildcard(t *testing.T) { + str := fmt.Sprintf("server*.domain %s", edKeyStr) + db := testDB(t, str) + + want := &KeyError{ + Want: []KnownKey{{ + Filename: "testdb", + Line: 1, + Key: edKey, + }}, + } + + got := db.check("server.domain:22", &net.TCPAddr{}, ecKey) + if !reflect.DeepEqual(got, want) { + t.Errorf("got %s, want %s", got, want) + } +} + +func TestLine(t *testing.T) { + for in, want := range map[string]string{ + "server.org": "server.org " + edKeyStr, + "server.org:22": "server.org " + edKeyStr, + "server.org:23": "[server.org]:23 " + edKeyStr, + "[c629:1ec4:102:304:102:304:102:304]:22": "[c629:1ec4:102:304:102:304:102:304] " + edKeyStr, + "[c629:1ec4:102:304:102:304:102:304]:23": "[c629:1ec4:102:304:102:304:102:304]:23 " + edKeyStr, + } { + if got := Line([]string{in}, edKey); got != want { + t.Errorf("Line(%q) = %q, want %q", in, got, want) + } + } +} + +func TestWildcardMatch(t *testing.T) { + for _, c := range []struct { + pat, str string + want bool + }{ + {"a?b", "abb", true}, + {"ab", "abc", false}, + {"abc", "ab", false}, + {"a*b", "axxxb", true}, + {"a*b", "axbxb", true}, + {"a*b", "axbxbc", false}, + {"a*?", "axbxc", true}, + {"a*b*", "axxbxxxxxx", true}, + {"a*b*c", "axxbxxxxxxc", true}, + {"a*b*?", "axxbxxxxxxc", true}, + {"a*b*z", "axxbxxbxxxz", true}, + {"a*b*z", "axxbxxzxxxz", true}, + {"a*b*z", "axxbxxzxxx", false}, + } { + got := wildcardMatch([]byte(c.pat), []byte(c.str)) + if got != c.want { + t.Errorf("wildcardMatch(%q, %q) = %v, want %v", c.pat, c.str, got, c.want) + } + + } +} + +// TODO(hanwen): test coverage for certificates. + +const testHostname = "hostname" + +// generated with keygen -H -f +const encodedTestHostnameHash = "|1|IHXZvQMvTcZTUU29+2vXFgx8Frs=|UGccIWfRVDwilMBnA3WJoRAC75Y=" + +func TestHostHash(t *testing.T) { + testHostHash(t, testHostname, encodedTestHostnameHash) +} + +func TestHashList(t *testing.T) { + encoded := HashHostname(testHostname) + testHostHash(t, testHostname, encoded) +} + +func testHostHash(t *testing.T, hostname, encoded string) { + typ, salt, hash, err := decodeHash(encoded) + if err != nil { + t.Fatalf("decodeHash: %v", err) + } + + if got := encodeHash(typ, salt, hash); got != encoded { + t.Errorf("got encoding %s want %s", got, encoded) + } + + if typ != sha1HashType { + t.Fatalf("got hash type %q, want %q", typ, sha1HashType) + } + + got := hashHost(hostname, salt) + if !bytes.Equal(got, hash) { + t.Errorf("got hash %x want %x", got, hash) + } +} + +func TestNormalize(t *testing.T) { + for in, want := range map[string]string{ + "127.0.0.1:22": "127.0.0.1", + "[127.0.0.1]:22": "127.0.0.1", + "[127.0.0.1]:23": "[127.0.0.1]:23", + "127.0.0.1:23": "[127.0.0.1]:23", + "[a.b.c]:22": "a.b.c", + "[abcd:abcd:abcd:abcd]": "[abcd:abcd:abcd:abcd]", + "[abcd:abcd:abcd:abcd]:22": "[abcd:abcd:abcd:abcd]", + "[abcd:abcd:abcd:abcd]:23": "[abcd:abcd:abcd:abcd]:23", + } { + got := Normalize(in) + if got != want { + t.Errorf("Normalize(%q) = %q, want %q", in, got, want) + } + } +} + +func TestHashedHostkeyCheck(t *testing.T) { + str := fmt.Sprintf("%s %s", HashHostname(testHostname), edKeyStr) + db := testDB(t, str) + if err := db.check(testHostname+":22", testAddr, edKey); err != nil { + t.Errorf("check(%s): %v", testHostname, err) + } + want := &KeyError{ + Want: []KnownKey{{ + Filename: "testdb", + Line: 1, + Key: edKey, + }}, + } + if got := db.check(testHostname+":22", testAddr, alternateEdKey); !reflect.DeepEqual(got, want) { + t.Errorf("got error %v, want %v", got, want) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index 77c84d165..8e95acc6a 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -45,6 +45,12 @@ type ServerConfig struct { // authenticating. NoClientAuth bool + // MaxAuthTries specifies the maximum number of authentication attempts + // permitted per connection. If set to a negative number, the number of + // attempts are unlimited. If set to zero, the number of attempts are limited + // to 6. + MaxAuthTries int + // PasswordCallback, if non-nil, is called when a user // attempts to authenticate using a password. PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error) @@ -141,6 +147,10 @@ type ServerConn struct { // Request and NewChannel channels must be serviced, or the connection // will hang. func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) { + if config.MaxAuthTries == 0 { + config.MaxAuthTries = 6 + } + fullConf := *config fullConf.SetDefaults() s := &connection{ @@ -267,8 +277,23 @@ func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, err var cache pubKeyCache var perms *Permissions + authFailures := 0 + userAuthLoop: for { + if authFailures >= config.MaxAuthTries && config.MaxAuthTries > 0 { + discMsg := &disconnectMsg{ + Reason: 2, + Message: "too many authentication failures", + } + + if err := s.transport.writePacket(Marshal(discMsg)); err != nil { + return nil, err + } + + return nil, discMsg + } + var userAuthReq userAuthRequestMsg if packet, err := s.transport.readPacket(); err != nil { return nil, err @@ -289,6 +314,11 @@ userAuthLoop: if config.NoClientAuth { authErr = nil } + + // allow initial attempt of 'none' without penalty + if authFailures == 0 { + authFailures-- + } case "password": if config.PasswordCallback == nil { authErr = errors.New("ssh: password auth not configured") @@ -360,6 +390,7 @@ userAuthLoop: if isQuery { // The client can query if the given public key // would be okay. + if len(payload) > 0 { return nil, parseError(msgUserAuthRequest) } @@ -409,6 +440,8 @@ userAuthLoop: break userAuthLoop } + authFailures++ + var failureMsg userAuthFailureMsg if config.PasswordCallback != nil { failureMsg.Methods = append(failureMsg.Methods, "password") diff --git a/vendor/golang.org/x/crypto/ssh/session_test.go b/vendor/golang.org/x/crypto/ssh/session_test.go index f35a378f2..7dce6dd69 100644 --- a/vendor/golang.org/x/crypto/ssh/session_test.go +++ b/vendor/golang.org/x/crypto/ssh/session_test.go @@ -59,7 +59,8 @@ func dial(handler serverType, t *testing.T) *Client { }() config := &ClientConfig{ - User: "testuser", + User: "testuser", + HostKeyCallback: InsecureIgnoreHostKey(), } conn, chans, reqs, err := NewClientConn(c2, "", config) @@ -641,7 +642,8 @@ func TestSessionID(t *testing.T) { } serverConf.AddHostKey(testSigners["ecdsa"]) clientConf := &ClientConfig{ - User: "user", + HostKeyCallback: InsecureIgnoreHostKey(), + User: "user", } go func() { @@ -747,7 +749,9 @@ func TestHostKeyAlgorithms(t *testing.T) { // By default, we get the preferred algorithm, which is ECDSA 256. - clientConf := &ClientConfig{} + clientConf := &ClientConfig{ + HostKeyCallback: InsecureIgnoreHostKey(), + } connect(clientConf, KeyAlgoECDSA256) // Client asks for RSA explicitly. diff --git a/vendor/golang.org/x/crypto/ssh/streamlocal.go b/vendor/golang.org/x/crypto/ssh/streamlocal.go new file mode 100644 index 000000000..a2dccc64c --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/streamlocal.go @@ -0,0 +1,115 @@ +package ssh + +import ( + "errors" + "io" + "net" +) + +// streamLocalChannelOpenDirectMsg is a struct used for SSH_MSG_CHANNEL_OPEN message +// with "direct-streamlocal@openssh.com" string. +// +// See openssh-portable/PROTOCOL, section 2.4. connection: Unix domain socket forwarding +// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL#L235 +type streamLocalChannelOpenDirectMsg struct { + socketPath string + reserved0 string + reserved1 uint32 +} + +// forwardedStreamLocalPayload is a struct used for SSH_MSG_CHANNEL_OPEN message +// with "forwarded-streamlocal@openssh.com" string. +type forwardedStreamLocalPayload struct { + SocketPath string + Reserved0 string +} + +// streamLocalChannelForwardMsg is a struct used for SSH2_MSG_GLOBAL_REQUEST message +// with "streamlocal-forward@openssh.com"/"cancel-streamlocal-forward@openssh.com" string. +type streamLocalChannelForwardMsg struct { + socketPath string +} + +// ListenUnix is similar to ListenTCP but uses a Unix domain socket. +func (c *Client) ListenUnix(socketPath string) (net.Listener, error) { + m := streamLocalChannelForwardMsg{ + socketPath, + } + // send message + ok, _, err := c.SendRequest("streamlocal-forward@openssh.com", true, Marshal(&m)) + if err != nil { + return nil, err + } + if !ok { + return nil, errors.New("ssh: streamlocal-forward@openssh.com request denied by peer") + } + ch := c.forwards.add(&net.UnixAddr{Name: socketPath, Net: "unix"}) + + return &unixListener{socketPath, c, ch}, nil +} + +func (c *Client) dialStreamLocal(socketPath string) (Channel, error) { + msg := streamLocalChannelOpenDirectMsg{ + socketPath: socketPath, + } + ch, in, err := c.OpenChannel("direct-streamlocal@openssh.com", Marshal(&msg)) + if err != nil { + return nil, err + } + go DiscardRequests(in) + return ch, err +} + +type unixListener struct { + socketPath string + + conn *Client + in <-chan forward +} + +// Accept waits for and returns the next connection to the listener. +func (l *unixListener) Accept() (net.Conn, error) { + s, ok := <-l.in + if !ok { + return nil, io.EOF + } + ch, incoming, err := s.newCh.Accept() + if err != nil { + return nil, err + } + go DiscardRequests(incoming) + + return &chanConn{ + Channel: ch, + laddr: &net.UnixAddr{ + Name: l.socketPath, + Net: "unix", + }, + raddr: &net.UnixAddr{ + Name: "@", + Net: "unix", + }, + }, nil +} + +// Close closes the listener. +func (l *unixListener) Close() error { + // this also closes the listener. + l.conn.forwards.remove(&net.UnixAddr{Name: l.socketPath, Net: "unix"}) + m := streamLocalChannelForwardMsg{ + l.socketPath, + } + ok, _, err := l.conn.SendRequest("cancel-streamlocal-forward@openssh.com", true, Marshal(&m)) + if err == nil && !ok { + err = errors.New("ssh: cancel-streamlocal-forward@openssh.com failed") + } + return err +} + +// Addr returns the listener's network address. +func (l *unixListener) Addr() net.Addr { + return &net.UnixAddr{ + Name: l.socketPath, + Net: "unix", + } +} diff --git a/vendor/golang.org/x/crypto/ssh/tcpip.go b/vendor/golang.org/x/crypto/ssh/tcpip.go index 6151241ff..acf17175d 100644 --- a/vendor/golang.org/x/crypto/ssh/tcpip.go +++ b/vendor/golang.org/x/crypto/ssh/tcpip.go @@ -20,12 +20,20 @@ import ( // addr. Incoming connections will be available by calling Accept on // the returned net.Listener. The listener must be serviced, or the // SSH connection may hang. +// N must be "tcp", "tcp4", "tcp6", or "unix". func (c *Client) Listen(n, addr string) (net.Listener, error) { - laddr, err := net.ResolveTCPAddr(n, addr) - if err != nil { - return nil, err + switch n { + case "tcp", "tcp4", "tcp6": + laddr, err := net.ResolveTCPAddr(n, addr) + if err != nil { + return nil, err + } + return c.ListenTCP(laddr) + case "unix": + return c.ListenUnix(addr) + default: + return nil, fmt.Errorf("ssh: unsupported protocol: %s", n) } - return c.ListenTCP(laddr) } // Automatic port allocation is broken with OpenSSH before 6.0. See @@ -116,7 +124,7 @@ func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { } // Register this forward, using the port number we obtained. - ch := c.forwards.add(*laddr) + ch := c.forwards.add(laddr) return &tcpListener{laddr, c, ch}, nil } @@ -131,7 +139,7 @@ type forwardList struct { // forwardEntry represents an established mapping of a laddr on a // remote ssh server to a channel connected to a tcpListener. type forwardEntry struct { - laddr net.TCPAddr + laddr net.Addr c chan forward } @@ -139,16 +147,16 @@ type forwardEntry struct { // arguments to add/remove/lookup should be address as specified in // the original forward-request. type forward struct { - newCh NewChannel // the ssh client channel underlying this forward - raddr *net.TCPAddr // the raddr of the incoming connection + newCh NewChannel // the ssh client channel underlying this forward + raddr net.Addr // the raddr of the incoming connection } -func (l *forwardList) add(addr net.TCPAddr) chan forward { +func (l *forwardList) add(addr net.Addr) chan forward { l.Lock() defer l.Unlock() f := forwardEntry{ - addr, - make(chan forward, 1), + laddr: addr, + c: make(chan forward, 1), } l.entries = append(l.entries, f) return f.c @@ -176,44 +184,69 @@ func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) { func (l *forwardList) handleChannels(in <-chan NewChannel) { for ch := range in { - var payload forwardedTCPPayload - if err := Unmarshal(ch.ExtraData(), &payload); err != nil { - ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error()) - continue + var ( + laddr net.Addr + raddr net.Addr + err error + ) + switch channelType := ch.ChannelType(); channelType { + case "forwarded-tcpip": + var payload forwardedTCPPayload + if err = Unmarshal(ch.ExtraData(), &payload); err != nil { + ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error()) + continue + } + + // RFC 4254 section 7.2 specifies that incoming + // addresses should list the address, in string + // format. It is implied that this should be an IP + // address, as it would be impossible to connect to it + // otherwise. + laddr, err = parseTCPAddr(payload.Addr, payload.Port) + if err != nil { + ch.Reject(ConnectionFailed, err.Error()) + continue + } + raddr, err = parseTCPAddr(payload.OriginAddr, payload.OriginPort) + if err != nil { + ch.Reject(ConnectionFailed, err.Error()) + continue + } + + case "forwarded-streamlocal@openssh.com": + var payload forwardedStreamLocalPayload + if err = Unmarshal(ch.ExtraData(), &payload); err != nil { + ch.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+err.Error()) + continue + } + laddr = &net.UnixAddr{ + Name: payload.SocketPath, + Net: "unix", + } + raddr = &net.UnixAddr{ + Name: "@", + Net: "unix", + } + default: + panic(fmt.Errorf("ssh: unknown channel type %s", channelType)) } - - // RFC 4254 section 7.2 specifies that incoming - // addresses should list the address, in string - // format. It is implied that this should be an IP - // address, as it would be impossible to connect to it - // otherwise. - laddr, err := parseTCPAddr(payload.Addr, payload.Port) - if err != nil { - ch.Reject(ConnectionFailed, err.Error()) - continue - } - raddr, err := parseTCPAddr(payload.OriginAddr, payload.OriginPort) - if err != nil { - ch.Reject(ConnectionFailed, err.Error()) - continue - } - - if ok := l.forward(*laddr, *raddr, ch); !ok { + if ok := l.forward(laddr, raddr, ch); !ok { // Section 7.2, implementations MUST reject spurious incoming // connections. ch.Reject(Prohibited, "no forward for address") continue } + } } // remove removes the forward entry, and the channel feeding its // listener. -func (l *forwardList) remove(addr net.TCPAddr) { +func (l *forwardList) remove(addr net.Addr) { l.Lock() defer l.Unlock() for i, f := range l.entries { - if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port { + if addr.Network() == f.laddr.Network() && addr.String() == f.laddr.String() { l.entries = append(l.entries[:i], l.entries[i+1:]...) close(f.c) return @@ -231,12 +264,12 @@ func (l *forwardList) closeAll() { l.entries = nil } -func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool { +func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool { l.Lock() defer l.Unlock() for _, f := range l.entries { - if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port { - f.c <- forward{ch, &raddr} + if laddr.Network() == f.laddr.Network() && laddr.String() == f.laddr.String() { + f.c <- forward{newCh: ch, raddr: raddr} return true } } @@ -262,7 +295,7 @@ func (l *tcpListener) Accept() (net.Conn, error) { } go DiscardRequests(incoming) - return &tcpChanConn{ + return &chanConn{ Channel: ch, laddr: l.laddr, raddr: s.raddr, @@ -277,7 +310,7 @@ func (l *tcpListener) Close() error { } // this also closes the listener. - l.conn.forwards.remove(*l.laddr) + l.conn.forwards.remove(l.laddr) ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m)) if err == nil && !ok { err = errors.New("ssh: cancel-tcpip-forward failed") @@ -293,29 +326,52 @@ func (l *tcpListener) Addr() net.Addr { // Dial initiates a connection to the addr from the remote host. // The resulting connection has a zero LocalAddr() and RemoteAddr(). func (c *Client) Dial(n, addr string) (net.Conn, error) { - // Parse the address into host and numeric port. - host, portString, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } - port, err := strconv.ParseUint(portString, 10, 16) - if err != nil { - return nil, err - } - // Use a zero address for local and remote address. - zeroAddr := &net.TCPAddr{ - IP: net.IPv4zero, - Port: 0, - } - ch, err := c.dial(net.IPv4zero.String(), 0, host, int(port)) - if err != nil { - return nil, err + var ch Channel + switch n { + case "tcp", "tcp4", "tcp6": + // Parse the address into host and numeric port. + host, portString, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + port, err := strconv.ParseUint(portString, 10, 16) + if err != nil { + return nil, err + } + ch, err = c.dial(net.IPv4zero.String(), 0, host, int(port)) + if err != nil { + return nil, err + } + // Use a zero address for local and remote address. + zeroAddr := &net.TCPAddr{ + IP: net.IPv4zero, + Port: 0, + } + return &chanConn{ + Channel: ch, + laddr: zeroAddr, + raddr: zeroAddr, + }, nil + case "unix": + var err error + ch, err = c.dialStreamLocal(addr) + if err != nil { + return nil, err + } + return &chanConn{ + Channel: ch, + laddr: &net.UnixAddr{ + Name: "@", + Net: "unix", + }, + raddr: &net.UnixAddr{ + Name: addr, + Net: "unix", + }, + }, nil + default: + return nil, fmt.Errorf("ssh: unsupported protocol: %s", n) } - return &tcpChanConn{ - Channel: ch, - laddr: zeroAddr, - raddr: zeroAddr, - }, nil } // DialTCP connects to the remote address raddr on the network net, @@ -332,7 +388,7 @@ func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) if err != nil { return nil, err } - return &tcpChanConn{ + return &chanConn{ Channel: ch, laddr: laddr, raddr: raddr, @@ -366,26 +422,26 @@ type tcpChan struct { Channel // the backing channel } -// tcpChanConn fulfills the net.Conn interface without +// chanConn fulfills the net.Conn interface without // the tcpChan having to hold laddr or raddr directly. -type tcpChanConn struct { +type chanConn struct { Channel laddr, raddr net.Addr } // LocalAddr returns the local network address. -func (t *tcpChanConn) LocalAddr() net.Addr { +func (t *chanConn) LocalAddr() net.Addr { return t.laddr } // RemoteAddr returns the remote network address. -func (t *tcpChanConn) RemoteAddr() net.Addr { +func (t *chanConn) RemoteAddr() net.Addr { return t.raddr } // SetDeadline sets the read and write deadlines associated // with the connection. -func (t *tcpChanConn) SetDeadline(deadline time.Time) error { +func (t *chanConn) SetDeadline(deadline time.Time) error { if err := t.SetReadDeadline(deadline); err != nil { return err } @@ -396,12 +452,14 @@ func (t *tcpChanConn) SetDeadline(deadline time.Time) error { // A zero value for t means Read will not time out. // After the deadline, the error from Read will implement net.Error // with Timeout() == true. -func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error { +func (t *chanConn) SetReadDeadline(deadline time.Time) error { + // for compatibility with previous version, + // the error message contains "tcpChan" return errors.New("ssh: tcpChan: deadline not supported") } // SetWriteDeadline exists to satisfy the net.Conn interface // but is not implemented by this type. It always returns an error. -func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error { +func (t *chanConn) SetWriteDeadline(deadline time.Time) error { return errors.New("ssh: tcpChan: deadline not supported") } diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go index 07eb5edd7..a2e1b57dc 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go @@ -14,14 +14,12 @@ import ( // State contains the state of a terminal. type State struct { - termios syscall.Termios + state *unix.Termios } // IsTerminal returns true if the given file descriptor is a terminal. func IsTerminal(fd int) bool { - // see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c - var termio unix.Termio - err := unix.IoctlSetTermio(fd, unix.TCGETA, &termio) + _, err := unix.IoctlGetTermio(fd, unix.TCGETA) return err == nil } @@ -71,3 +69,60 @@ func ReadPassword(fd int) ([]byte, error) { return ret, nil } + +// MakeRaw puts the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +// see http://cr.illumos.org/~webrev/andy_js/1060/ +func MakeRaw(fd int) (*State, error) { + oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS) + if err != nil { + return nil, err + } + oldTermios := *oldTermiosPtr + + newTermios := oldTermios + newTermios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON + newTermios.Oflag &^= syscall.OPOST + newTermios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN + newTermios.Cflag &^= syscall.CSIZE | syscall.PARENB + newTermios.Cflag |= syscall.CS8 + newTermios.Cc[unix.VMIN] = 1 + newTermios.Cc[unix.VTIME] = 0 + + if err := unix.IoctlSetTermios(fd, unix.TCSETS, &newTermios); err != nil { + return nil, err + } + + return &State{ + state: oldTermiosPtr, + }, nil +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, oldState *State) error { + return unix.IoctlSetTermios(fd, unix.TCSETS, oldState.state) +} + +// GetState returns the current state of a terminal which may be useful to +// restore the terminal after a signal. +func GetState(fd int) (*State, error) { + oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS) + if err != nil { + return nil, err + } + + return &State{ + state: oldTermiosPtr, + }, nil +} + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (width, height int, err error) { + ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) + if err != nil { + return 0, 0, err + } + return int(ws.Col), int(ws.Row), nil +} diff --git a/vendor/golang.org/x/crypto/ssh/test/cert_test.go b/vendor/golang.org/x/crypto/ssh/test/cert_test.go index 364790f17..bc83e4f5d 100644 --- a/vendor/golang.org/x/crypto/ssh/test/cert_test.go +++ b/vendor/golang.org/x/crypto/ssh/test/cert_test.go @@ -36,7 +36,8 @@ func TestCertLogin(t *testing.T) { } conf := &ssh.ClientConfig{ - User: username(), + User: username(), + HostKeyCallback: ssh.InsecureIgnoreHostKey(), } conf.Auth = append(conf.Auth, ssh.PublicKeys(certSigner)) client, err := s.TryDial(conf) diff --git a/vendor/golang.org/x/crypto/ssh/test/dial_unix_test.go b/vendor/golang.org/x/crypto/ssh/test/dial_unix_test.go new file mode 100644 index 000000000..091e48cc1 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/test/dial_unix_test.go @@ -0,0 +1,128 @@ +// 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. + +// +build !windows + +package test + +// direct-tcpip and direct-streamlocal functional tests + +import ( + "fmt" + "io" + "io/ioutil" + "net" + "strings" + "testing" +) + +type dialTester interface { + TestServerConn(t *testing.T, c net.Conn) + TestClientConn(t *testing.T, c net.Conn) +} + +func testDial(t *testing.T, n, listenAddr string, x dialTester) { + server := newServer(t) + defer server.Shutdown() + sshConn := server.Dial(clientConfig()) + defer sshConn.Close() + + l, err := net.Listen(n, listenAddr) + if err != nil { + t.Fatalf("Listen: %v", err) + } + defer l.Close() + + testData := fmt.Sprintf("hello from %s, %s", n, listenAddr) + go func() { + for { + c, err := l.Accept() + if err != nil { + break + } + x.TestServerConn(t, c) + + io.WriteString(c, testData) + c.Close() + } + }() + + conn, err := sshConn.Dial(n, l.Addr().String()) + if err != nil { + t.Fatalf("Dial: %v", err) + } + x.TestClientConn(t, conn) + defer conn.Close() + b, err := ioutil.ReadAll(conn) + if err != nil { + t.Fatalf("ReadAll: %v", err) + } + t.Logf("got %q", string(b)) + if string(b) != testData { + t.Fatalf("expected %q, got %q", testData, string(b)) + } +} + +type tcpDialTester struct { + listenAddr string +} + +func (x *tcpDialTester) TestServerConn(t *testing.T, c net.Conn) { + host := strings.Split(x.listenAddr, ":")[0] + prefix := host + ":" + if !strings.HasPrefix(c.LocalAddr().String(), prefix) { + t.Fatalf("expected to start with %q, got %q", prefix, c.LocalAddr().String()) + } + if !strings.HasPrefix(c.RemoteAddr().String(), prefix) { + t.Fatalf("expected to start with %q, got %q", prefix, c.RemoteAddr().String()) + } +} + +func (x *tcpDialTester) TestClientConn(t *testing.T, c net.Conn) { + // we use zero addresses. see *Client.Dial. + if c.LocalAddr().String() != "0.0.0.0:0" { + t.Fatalf("expected \"0.0.0.0:0\", got %q", c.LocalAddr().String()) + } + if c.RemoteAddr().String() != "0.0.0.0:0" { + t.Fatalf("expected \"0.0.0.0:0\", got %q", c.RemoteAddr().String()) + } +} + +func TestDialTCP(t *testing.T) { + x := &tcpDialTester{ + listenAddr: "127.0.0.1:0", + } + testDial(t, "tcp", x.listenAddr, x) +} + +type unixDialTester struct { + listenAddr string +} + +func (x *unixDialTester) TestServerConn(t *testing.T, c net.Conn) { + if c.LocalAddr().String() != x.listenAddr { + t.Fatalf("expected %q, got %q", x.listenAddr, c.LocalAddr().String()) + } + if c.RemoteAddr().String() != "@" { + t.Fatalf("expected \"@\", got %q", c.RemoteAddr().String()) + } +} + +func (x *unixDialTester) TestClientConn(t *testing.T, c net.Conn) { + if c.RemoteAddr().String() != x.listenAddr { + t.Fatalf("expected %q, got %q", x.listenAddr, c.RemoteAddr().String()) + } + if c.LocalAddr().String() != "@" { + t.Fatalf("expected \"@\", got %q", c.LocalAddr().String()) + } +} + +func TestDialUnix(t *testing.T) { + addr, cleanup := newTempSocket(t) + defer cleanup() + x := &unixDialTester{ + listenAddr: addr, + } + testDial(t, "unix", x.listenAddr, x) +} diff --git a/vendor/golang.org/x/crypto/ssh/test/forward_unix_test.go b/vendor/golang.org/x/crypto/ssh/test/forward_unix_test.go index 877a88cde..ea8193780 100644 --- a/vendor/golang.org/x/crypto/ssh/test/forward_unix_test.go +++ b/vendor/golang.org/x/crypto/ssh/test/forward_unix_test.go @@ -16,13 +16,17 @@ import ( "time" ) -func TestPortForward(t *testing.T) { +type closeWriter interface { + CloseWrite() error +} + +func testPortForward(t *testing.T, n, listenAddr string) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) defer conn.Close() - sshListener, err := conn.Listen("tcp", "localhost:0") + sshListener, err := conn.Listen(n, listenAddr) if err != nil { t.Fatal(err) } @@ -41,14 +45,14 @@ func TestPortForward(t *testing.T) { }() forwardedAddr := sshListener.Addr().String() - tcpConn, err := net.Dial("tcp", forwardedAddr) + netConn, err := net.Dial(n, forwardedAddr) if err != nil { - t.Fatalf("TCP dial failed: %v", err) + t.Fatalf("net dial failed: %v", err) } readChan := make(chan []byte) go func() { - data, _ := ioutil.ReadAll(tcpConn) + data, _ := ioutil.ReadAll(netConn) readChan <- data }() @@ -62,14 +66,14 @@ func TestPortForward(t *testing.T) { for len(sent) < 1000*1000 { // Send random sized chunks m := rand.Intn(len(data)) - n, err := tcpConn.Write(data[:m]) + n, err := netConn.Write(data[:m]) if err != nil { break } sent = append(sent, data[:n]...) } - if err := tcpConn.(*net.TCPConn).CloseWrite(); err != nil { - t.Errorf("tcpConn.CloseWrite: %v", err) + if err := netConn.(closeWriter).CloseWrite(); err != nil { + t.Errorf("netConn.CloseWrite: %v", err) } read := <-readChan @@ -86,19 +90,29 @@ func TestPortForward(t *testing.T) { } // Check that the forward disappeared. - tcpConn, err = net.Dial("tcp", forwardedAddr) + netConn, err = net.Dial(n, forwardedAddr) if err == nil { - tcpConn.Close() + netConn.Close() t.Errorf("still listening to %s after closing", forwardedAddr) } } -func TestAcceptClose(t *testing.T) { +func TestPortForwardTCP(t *testing.T) { + testPortForward(t, "tcp", "localhost:0") +} + +func TestPortForwardUnix(t *testing.T) { + addr, cleanup := newTempSocket(t) + defer cleanup() + testPortForward(t, "unix", addr) +} + +func testAcceptClose(t *testing.T, n, listenAddr string) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) - sshListener, err := conn.Listen("tcp", "localhost:0") + sshListener, err := conn.Listen(n, listenAddr) if err != nil { t.Fatal(err) } @@ -124,13 +138,23 @@ func TestAcceptClose(t *testing.T) { } } +func TestAcceptCloseTCP(t *testing.T) { + testAcceptClose(t, "tcp", "localhost:0") +} + +func TestAcceptCloseUnix(t *testing.T) { + addr, cleanup := newTempSocket(t) + defer cleanup() + testAcceptClose(t, "unix", addr) +} + // Check that listeners exit if the underlying client transport dies. -func TestPortForwardConnectionClose(t *testing.T) { +func testPortForwardConnectionClose(t *testing.T, n, listenAddr string) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) - sshListener, err := conn.Listen("tcp", "localhost:0") + sshListener, err := conn.Listen(n, listenAddr) if err != nil { t.Fatal(err) } @@ -158,3 +182,13 @@ func TestPortForwardConnectionClose(t *testing.T) { t.Logf("quit as expected (error %v)", err) } } + +func TestPortForwardConnectionCloseTCP(t *testing.T) { + testPortForwardConnectionClose(t, "tcp", "localhost:0") +} + +func TestPortForwardConnectionCloseUnix(t *testing.T) { + addr, cleanup := newTempSocket(t) + defer cleanup() + testPortForwardConnectionClose(t, "unix", addr) +} diff --git a/vendor/golang.org/x/crypto/ssh/test/tcpip_test.go b/vendor/golang.org/x/crypto/ssh/test/tcpip_test.go deleted file mode 100644 index a2eb9358d..000000000 --- a/vendor/golang.org/x/crypto/ssh/test/tcpip_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// 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. - -// +build !windows - -package test - -// direct-tcpip functional tests - -import ( - "io" - "net" - "testing" -) - -func TestDial(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - sshConn := server.Dial(clientConfig()) - defer sshConn.Close() - - l, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("Listen: %v", err) - } - defer l.Close() - - go func() { - for { - c, err := l.Accept() - if err != nil { - break - } - - io.WriteString(c, c.RemoteAddr().String()) - c.Close() - } - }() - - conn, err := sshConn.Dial("tcp", l.Addr().String()) - if err != nil { - t.Fatalf("Dial: %v", err) - } - defer conn.Close() -} diff --git a/vendor/golang.org/x/crypto/ssh/test/test_unix_test.go b/vendor/golang.org/x/crypto/ssh/test/test_unix_test.go index 3bfd881e4..dd9ff4003 100644 --- a/vendor/golang.org/x/crypto/ssh/test/test_unix_test.go +++ b/vendor/golang.org/x/crypto/ssh/test/test_unix_test.go @@ -266,3 +266,13 @@ func newServer(t *testing.T) *server { }, } } + +func newTempSocket(t *testing.T) (string, func()) { + dir, err := ioutil.TempDir("", "socket") + if err != nil { + t.Fatal(err) + } + deferFunc := func() { os.RemoveAll(dir) } + addr := filepath.Join(dir, "sock") + return addr, deferFunc +} diff --git a/vendor/golang.org/x/crypto/ssh/testdata/keys.go b/vendor/golang.org/x/crypto/ssh/testdata/keys.go index 736dad951..0be2e7e65 100644 --- a/vendor/golang.org/x/crypto/ssh/testdata/keys.go +++ b/vendor/golang.org/x/crypto/ssh/testdata/keys.go @@ -48,6 +48,22 @@ AAAEAaYmXltfW6nhRo3iWGglRB48lYq0z0Q3I3KyrdutEr6j7d/uFLuDlRbBc4ZVOsx+Gb HKuOrPtLHFvHsjWPwO+/AAAAE2dhcnRvbm1AZ2FydG9ubS14cHMBAg== -----END OPENSSH PRIVATE KEY----- `), + "rsa-openssh-format": []byte(`-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn +NhAAAAAwEAAQAAAIEAwa48yfWFi3uIdqzuf9X7C2Zxfea/Iaaw0zIwHudpF8U92WVIiC5l +oEuW1+OaVi3UWfIEjWMV1tHGysrHOwtwc34BPCJqJknUQO/KtDTBTJ4Pryhw1bWPC999Lz +a+yrCTdNQYBzoROXKExZgPFh9pTMi5wqpHDuOQ2qZFIEI3lT0AAAIQWL0H31i9B98AAAAH +c3NoLXJzYQAAAIEAwa48yfWFi3uIdqzuf9X7C2Zxfea/Iaaw0zIwHudpF8U92WVIiC5loE +uW1+OaVi3UWfIEjWMV1tHGysrHOwtwc34BPCJqJknUQO/KtDTBTJ4Pryhw1bWPC999Lza+ +yrCTdNQYBzoROXKExZgPFh9pTMi5wqpHDuOQ2qZFIEI3lT0AAAADAQABAAAAgCThyTGsT4 +IARDxVMhWl6eiB2ZrgFgWSeJm/NOqtppWgOebsIqPMMg4UVuVFsl422/lE3RkPhVkjGXgE +pWvZAdCnmLmApK8wK12vF334lZhZT7t3Z9EzJps88PWEHo7kguf285HcnUM7FlFeissJdk +kXly34y7/3X/a6Tclm+iABAAAAQE0xR/KxZ39slwfMv64Rz7WKk1PPskaryI29aHE3mKHk +pY2QA+P3QlrKxT/VWUMjHUbNNdYfJm48xu0SGNMRdKMAAABBAORh2NP/06JUV3J9W/2Hju +X1ViJuqqcQnJPVzpgSL826EC2xwOECTqoY8uvFpUdD7CtpksIxNVqRIhuNOlz0lqEAAABB +ANkaHTTaPojClO0dKJ/Zjs7pWOCGliebBYprQ/Y4r9QLBkC/XaWMS26gFIrjgC7D2Rv+rZ +wSD0v0RcmkITP1ZR0AAAAYcHF1ZXJuYUBMdWNreUh5ZHJvLmxvY2FsAQID +-----END OPENSSH PRIVATE KEY-----`), "user": []byte(`-----BEGIN EC PRIVATE KEY----- MHcCAQEEILYCAeq8f7V4vSSypRw7pxy8yz3V5W4qg8kSC3zJhqpQoAoGCCqGSM49 AwEHoUQDQgAEYcO2xNKiRUYOLEHM7VYAp57HNyKbOdYtHD83Z4hzNPVC4tM5mdGD |