mirror of https://github.com/k3s-io/k3s
parent
d97f125ddf
commit
a01a493d5d
|
@ -2358,31 +2358,39 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/crypto/bcrypt",
|
"ImportPath": "golang.org/x/crypto/bcrypt",
|
||||||
"Rev": "1f22c0103821b9390939b6776727195525381532"
|
"Rev": "d172538b2cfce0c13cee31e647d0367aa8cd2486"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/crypto/blowfish",
|
"ImportPath": "golang.org/x/crypto/blowfish",
|
||||||
"Rev": "1f22c0103821b9390939b6776727195525381532"
|
"Rev": "d172538b2cfce0c13cee31e647d0367aa8cd2486"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/crypto/curve25519",
|
"ImportPath": "golang.org/x/crypto/curve25519",
|
||||||
"Rev": "1f22c0103821b9390939b6776727195525381532"
|
"Rev": "d172538b2cfce0c13cee31e647d0367aa8cd2486"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/crypto/ed25519",
|
||||||
|
"Rev": "d172538b2cfce0c13cee31e647d0367aa8cd2486"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/crypto/ed25519/internal/edwards25519",
|
||||||
|
"Rev": "d172538b2cfce0c13cee31e647d0367aa8cd2486"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/crypto/pkcs12",
|
"ImportPath": "golang.org/x/crypto/pkcs12",
|
||||||
"Rev": "1f22c0103821b9390939b6776727195525381532"
|
"Rev": "d172538b2cfce0c13cee31e647d0367aa8cd2486"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/crypto/pkcs12/internal/rc2",
|
"ImportPath": "golang.org/x/crypto/pkcs12/internal/rc2",
|
||||||
"Rev": "1f22c0103821b9390939b6776727195525381532"
|
"Rev": "d172538b2cfce0c13cee31e647d0367aa8cd2486"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/crypto/ssh",
|
"ImportPath": "golang.org/x/crypto/ssh",
|
||||||
"Rev": "1f22c0103821b9390939b6776727195525381532"
|
"Rev": "d172538b2cfce0c13cee31e647d0367aa8cd2486"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/crypto/ssh/terminal",
|
"ImportPath": "golang.org/x/crypto/ssh/terminal",
|
||||||
"Rev": "1f22c0103821b9390939b6776727195525381532"
|
"Rev": "d172538b2cfce0c13cee31e647d0367aa8cd2486"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/exp/inotify",
|
"ImportPath": "golang.org/x/exp/inotify",
|
||||||
|
|
|
@ -73393,6 +73393,76 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
= vendor/golang.org/x/crypto/ed25519 licensed under: =
|
||||||
|
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
= vendor/golang.org/x/crypto/LICENSE 5d4950ecb7b26d2c5e4e7b4e0dd74707 -
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
= vendor/golang.org/x/crypto/ed25519/internal/edwards25519 licensed under: =
|
||||||
|
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
= vendor/golang.org/x/crypto/LICENSE 5d4950ecb7b26d2c5e4e7b4e0dd74707 -
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
= vendor/golang.org/x/crypto/pkcs12 licensed under: =
|
= vendor/golang.org/x/crypto/pkcs12 licensed under: =
|
||||||
|
|
||||||
|
|
|
@ -7016,7 +7016,10 @@ go_library(
|
||||||
"golang.org/x/crypto/ssh/transport.go",
|
"golang.org/x/crypto/ssh/transport.go",
|
||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = ["//vendor:golang.org/x/crypto/curve25519"],
|
deps = [
|
||||||
|
"//vendor:golang.org/x/crypto/curve25519",
|
||||||
|
"//vendor:golang.org/x/crypto/ed25519",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
|
@ -12184,3 +12187,19 @@ go_library(
|
||||||
"//pkg/api/v1:go_default_library",
|
"//pkg/api/v1:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "golang.org/x/crypto/ed25519",
|
||||||
|
srcs = ["golang.org/x/crypto/ed25519/ed25519.go"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = ["//vendor:golang.org/x/crypto/ed25519/internal/edwards25519"],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "golang.org/x/crypto/ed25519/internal/edwards25519",
|
||||||
|
srcs = [
|
||||||
|
"golang.org/x/crypto/ed25519/internal/edwards25519/const.go",
|
||||||
|
"golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go",
|
||||||
|
],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
)
|
||||||
|
|
|
@ -39,7 +39,7 @@ func NewCipher(key []byte) (*Cipher, error) {
|
||||||
|
|
||||||
// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
|
// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
|
||||||
// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
|
// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
|
||||||
// sufficient and desirable. For bcrypt compatiblity, the key can be over 56
|
// sufficient and desirable. For bcrypt compatibility, the key can be over 56
|
||||||
// bytes.
|
// bytes.
|
||||||
func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
|
func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
|
||||||
if len(salt) == 0 {
|
if len(salt) == 0 {
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
// Copyright 2016 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 ed25519 implements the Ed25519 signature algorithm. See
|
||||||
|
// http://ed25519.cr.yp.to/.
|
||||||
|
//
|
||||||
|
// These functions are also compatible with the “Ed25519” function defined in
|
||||||
|
// https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-05.
|
||||||
|
package ed25519
|
||||||
|
|
||||||
|
// This code is a port of the public domain, “ref10” implementation of ed25519
|
||||||
|
// from SUPERCOP.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
cryptorand "crypto/rand"
|
||||||
|
"crypto/sha512"
|
||||||
|
"crypto/subtle"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ed25519/internal/edwards25519"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PublicKeySize is the size, in bytes, of public keys as used in this package.
|
||||||
|
PublicKeySize = 32
|
||||||
|
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
|
||||||
|
PrivateKeySize = 64
|
||||||
|
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||||
|
SignatureSize = 64
|
||||||
|
)
|
||||||
|
|
||||||
|
// PublicKey is the type of Ed25519 public keys.
|
||||||
|
type PublicKey []byte
|
||||||
|
|
||||||
|
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
|
||||||
|
type PrivateKey []byte
|
||||||
|
|
||||||
|
// Public returns the PublicKey corresponding to priv.
|
||||||
|
func (priv PrivateKey) Public() crypto.PublicKey {
|
||||||
|
publicKey := make([]byte, PublicKeySize)
|
||||||
|
copy(publicKey, priv[32:])
|
||||||
|
return PublicKey(publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign signs the given message with priv.
|
||||||
|
// Ed25519 performs two passes over messages to be signed and therefore cannot
|
||||||
|
// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
|
||||||
|
// indicate the message hasn't been hashed. This can be achieved by passing
|
||||||
|
// crypto.Hash(0) as the value for opts.
|
||||||
|
func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||||
|
if opts.HashFunc() != crypto.Hash(0) {
|
||||||
|
return nil, errors.New("ed25519: cannot sign hashed message")
|
||||||
|
}
|
||||||
|
|
||||||
|
return Sign(priv, message), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateKey generates a public/private key pair using entropy from rand.
|
||||||
|
// If rand is nil, crypto/rand.Reader will be used.
|
||||||
|
func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) {
|
||||||
|
if rand == nil {
|
||||||
|
rand = cryptorand.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
privateKey = make([]byte, PrivateKeySize)
|
||||||
|
publicKey = make([]byte, PublicKeySize)
|
||||||
|
_, err = io.ReadFull(rand, privateKey[:32])
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
digest := sha512.Sum512(privateKey[:32])
|
||||||
|
digest[0] &= 248
|
||||||
|
digest[31] &= 127
|
||||||
|
digest[31] |= 64
|
||||||
|
|
||||||
|
var A edwards25519.ExtendedGroupElement
|
||||||
|
var hBytes [32]byte
|
||||||
|
copy(hBytes[:], digest[:])
|
||||||
|
edwards25519.GeScalarMultBase(&A, &hBytes)
|
||||||
|
var publicKeyBytes [32]byte
|
||||||
|
A.ToBytes(&publicKeyBytes)
|
||||||
|
|
||||||
|
copy(privateKey[32:], publicKeyBytes[:])
|
||||||
|
copy(publicKey, publicKeyBytes[:])
|
||||||
|
|
||||||
|
return publicKey, privateKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign signs the message with privateKey and returns a signature. It will
|
||||||
|
// panic if len(privateKey) is not PrivateKeySize.
|
||||||
|
func Sign(privateKey PrivateKey, message []byte) []byte {
|
||||||
|
if l := len(privateKey); l != PrivateKeySize {
|
||||||
|
panic("ed25519: bad private key length: " + strconv.Itoa(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
h := sha512.New()
|
||||||
|
h.Write(privateKey[:32])
|
||||||
|
|
||||||
|
var digest1, messageDigest, hramDigest [64]byte
|
||||||
|
var expandedSecretKey [32]byte
|
||||||
|
h.Sum(digest1[:0])
|
||||||
|
copy(expandedSecretKey[:], digest1[:])
|
||||||
|
expandedSecretKey[0] &= 248
|
||||||
|
expandedSecretKey[31] &= 63
|
||||||
|
expandedSecretKey[31] |= 64
|
||||||
|
|
||||||
|
h.Reset()
|
||||||
|
h.Write(digest1[32:])
|
||||||
|
h.Write(message)
|
||||||
|
h.Sum(messageDigest[:0])
|
||||||
|
|
||||||
|
var messageDigestReduced [32]byte
|
||||||
|
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
|
||||||
|
var R edwards25519.ExtendedGroupElement
|
||||||
|
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
|
||||||
|
|
||||||
|
var encodedR [32]byte
|
||||||
|
R.ToBytes(&encodedR)
|
||||||
|
|
||||||
|
h.Reset()
|
||||||
|
h.Write(encodedR[:])
|
||||||
|
h.Write(privateKey[32:])
|
||||||
|
h.Write(message)
|
||||||
|
h.Sum(hramDigest[:0])
|
||||||
|
var hramDigestReduced [32]byte
|
||||||
|
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
|
||||||
|
|
||||||
|
var s [32]byte
|
||||||
|
edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
|
||||||
|
|
||||||
|
signature := make([]byte, SignatureSize)
|
||||||
|
copy(signature[:], encodedR[:])
|
||||||
|
copy(signature[32:], s[:])
|
||||||
|
|
||||||
|
return signature
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify reports whether sig is a valid signature of message by publicKey. It
|
||||||
|
// will panic if len(publicKey) is not PublicKeySize.
|
||||||
|
func Verify(publicKey PublicKey, message, sig []byte) bool {
|
||||||
|
if l := len(publicKey); l != PublicKeySize {
|
||||||
|
panic("ed25519: bad public key length: " + strconv.Itoa(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sig) != SignatureSize || sig[63]&224 != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var A edwards25519.ExtendedGroupElement
|
||||||
|
var publicKeyBytes [32]byte
|
||||||
|
copy(publicKeyBytes[:], publicKey)
|
||||||
|
if !A.FromBytes(&publicKeyBytes) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
edwards25519.FeNeg(&A.X, &A.X)
|
||||||
|
edwards25519.FeNeg(&A.T, &A.T)
|
||||||
|
|
||||||
|
h := sha512.New()
|
||||||
|
h.Write(sig[:32])
|
||||||
|
h.Write(publicKey[:])
|
||||||
|
h.Write(message)
|
||||||
|
var digest [64]byte
|
||||||
|
h.Sum(digest[:0])
|
||||||
|
|
||||||
|
var hReduced [32]byte
|
||||||
|
edwards25519.ScReduce(&hReduced, &digest)
|
||||||
|
|
||||||
|
var R edwards25519.ProjectiveGroupElement
|
||||||
|
var b [32]byte
|
||||||
|
copy(b[:], sig[32:])
|
||||||
|
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
|
||||||
|
|
||||||
|
var checkR [32]byte
|
||||||
|
R.ToBytes(&checkR)
|
||||||
|
return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1
|
||||||
|
}
|
1422
vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go
generated
vendored
Normal file
1422
vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1771
vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go
generated
vendored
Normal file
1771
vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -22,6 +22,7 @@ const (
|
||||||
CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
||||||
CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
|
CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
|
||||||
CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
|
CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
|
||||||
|
CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Certificate types distinguish between host and user
|
// Certificate types distinguish between host and user
|
||||||
|
@ -401,6 +402,7 @@ var certAlgoNames = map[string]string{
|
||||||
KeyAlgoECDSA256: CertAlgoECDSA256v01,
|
KeyAlgoECDSA256: CertAlgoECDSA256v01,
|
||||||
KeyAlgoECDSA384: CertAlgoECDSA384v01,
|
KeyAlgoECDSA384: CertAlgoECDSA384v01,
|
||||||
KeyAlgoECDSA521: CertAlgoECDSA521v01,
|
KeyAlgoECDSA521: CertAlgoECDSA521v01,
|
||||||
|
KeyAlgoED25519: CertAlgoED25519v01,
|
||||||
}
|
}
|
||||||
|
|
||||||
// certToPrivAlgo returns the underlying algorithm for a certificate algorithm.
|
// certToPrivAlgo returns the underlying algorithm for a certificate algorithm.
|
||||||
|
@ -459,7 +461,7 @@ func (c *Certificate) Marshal() []byte {
|
||||||
func (c *Certificate) Type() string {
|
func (c *Certificate) Type() string {
|
||||||
algo, ok := certAlgoNames[c.Key.Type()]
|
algo, ok := certAlgoNames[c.Key.Type()]
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("unknown cert key type")
|
panic("unknown cert key type " + c.Key.Type())
|
||||||
}
|
}
|
||||||
return algo
|
return algo
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,8 @@ type Channel interface {
|
||||||
// boolean, otherwise the return value will be false. Channel
|
// boolean, otherwise the return value will be false. Channel
|
||||||
// requests are out-of-band messages so they may be sent even
|
// requests are out-of-band messages so they may be sent even
|
||||||
// if the data stream is closed or blocked by flow control.
|
// if the data stream is closed or blocked by flow control.
|
||||||
|
// If the channel is closed before a reply is returned, io.EOF
|
||||||
|
// is returned.
|
||||||
SendRequest(name string, wantReply bool, payload []byte) (bool, error)
|
SendRequest(name string, wantReply bool, payload []byte) (bool, error)
|
||||||
|
|
||||||
// Stderr returns an io.ReadWriter that writes to this channel
|
// Stderr returns an io.ReadWriter that writes to this channel
|
||||||
|
@ -217,7 +219,7 @@ func (c *channel) writePacket(packet []byte) error {
|
||||||
|
|
||||||
func (c *channel) sendMessage(msg interface{}) error {
|
func (c *channel) sendMessage(msg interface{}) error {
|
||||||
if debugMux {
|
if debugMux {
|
||||||
log.Printf("send %d: %#v", c.mux.chanList.offset, msg)
|
log.Printf("send(%d): %#v", c.mux.chanList.offset, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
p := Marshal(msg)
|
p := Marshal(msg)
|
||||||
|
@ -371,7 +373,7 @@ func (c *channel) close() {
|
||||||
close(c.msg)
|
close(c.msg)
|
||||||
close(c.incomingRequests)
|
close(c.incomingRequests)
|
||||||
c.writeMu.Lock()
|
c.writeMu.Lock()
|
||||||
// This is not necesary for a normal channel teardown, but if
|
// This is not necessary for a normal channel teardown, but if
|
||||||
// there was another error, it is.
|
// there was another error, it is.
|
||||||
c.sentClose = true
|
c.sentClose = true
|
||||||
c.writeMu.Unlock()
|
c.writeMu.Unlock()
|
||||||
|
|
|
@ -7,6 +7,7 @@ package ssh
|
||||||
import (
|
import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/des"
|
||||||
"crypto/rc4"
|
"crypto/rc4"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
@ -121,6 +122,9 @@ var cipherModes = map[string]*streamCipherMode{
|
||||||
// You should expect that an active attacker can recover plaintext if
|
// You should expect that an active attacker can recover plaintext if
|
||||||
// you do.
|
// you do.
|
||||||
aes128cbcID: {16, aes.BlockSize, 0, nil},
|
aes128cbcID: {16, aes.BlockSize, 0, nil},
|
||||||
|
|
||||||
|
// 3des-cbc is insecure and is disabled by default.
|
||||||
|
tripledescbcID: {24, des.BlockSize, 0, nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
// prefixLen is the length of the packet prefix that contains the packet length
|
// prefixLen is the length of the packet prefix that contains the packet length
|
||||||
|
@ -368,12 +372,7 @@ type cbcCipher struct {
|
||||||
oracleCamouflage uint32
|
oracleCamouflage uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||||
c, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cbc := &cbcCipher{
|
cbc := &cbcCipher{
|
||||||
mac: macModes[algs.MAC].new(macKey),
|
mac: macModes[algs.MAC].new(macKey),
|
||||||
decrypter: cipher.NewCBCDecrypter(c, iv),
|
decrypter: cipher.NewCBCDecrypter(c, iv),
|
||||||
|
@ -387,6 +386,34 @@ func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCi
|
||||||
return cbc, nil
|
return cbc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||||
|
c, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cbc, err := newCBCCipher(c, iv, key, macKey, algs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cbc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTripleDESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||||
|
c, err := des.NewTripleDESCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cbc, err := newCBCCipher(c, iv, key, macKey, algs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cbc, nil
|
||||||
|
}
|
||||||
|
|
||||||
func maxUInt32(a, b int) uint32 {
|
func maxUInt32(a, b int) uint32 {
|
||||||
if a > b {
|
if a > b {
|
||||||
return uint32(a)
|
return uint32(a)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client implements a traditional SSH client that supports shells,
|
// Client implements a traditional SSH client that supports shells,
|
||||||
|
@ -96,16 +97,10 @@ func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) e
|
||||||
c.transport = newClientTransport(
|
c.transport = newClientTransport(
|
||||||
newTransport(c.sshConn.conn, config.Rand, true /* is client */),
|
newTransport(c.sshConn.conn, config.Rand, true /* is client */),
|
||||||
c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr())
|
c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr())
|
||||||
if err := c.transport.requestKeyChange(); err != nil {
|
if err := c.transport.requestInitialKeyChange(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if packet, err := c.transport.readPacket(); err != nil {
|
|
||||||
return err
|
|
||||||
} else if packet[0] != msgNewKeys {
|
|
||||||
return unexpectedMessageError(msgNewKeys, packet[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// We just did the key change, so the session ID is established.
|
// We just did the key change, so the session ID is established.
|
||||||
c.sessionID = c.transport.getSessionID()
|
c.sessionID = c.transport.getSessionID()
|
||||||
|
|
||||||
|
@ -169,7 +164,7 @@ func (c *Client) handleChannelOpens(in <-chan NewChannel) {
|
||||||
// to incoming channels and requests, use net.Dial with NewClientConn
|
// to incoming channels and requests, use net.Dial with NewClientConn
|
||||||
// instead.
|
// instead.
|
||||||
func Dial(network, addr string, config *ClientConfig) (*Client, error) {
|
func Dial(network, addr string, config *ClientConfig) (*Client, error) {
|
||||||
conn, err := net.Dial(network, addr)
|
conn, err := net.DialTimeout(network, addr, config.Timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -210,4 +205,9 @@ type ClientConfig struct {
|
||||||
// string returned from PublicKey.Type method may be used, or
|
// string returned from PublicKey.Type method may be used, or
|
||||||
// any of the CertAlgoXxxx and KeyAlgoXxxx constants.
|
// any of the CertAlgoXxxx and KeyAlgoXxxx constants.
|
||||||
HostKeyAlgorithms []string
|
HostKeyAlgorithms []string
|
||||||
|
|
||||||
|
// Timeout is the maximum amount of time for the TCP connection to establish.
|
||||||
|
//
|
||||||
|
// A Timeout of zero means no timeout.
|
||||||
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
|
@ -321,8 +321,6 @@ func handleAuthResponse(c packetConn) (bool, []string, error) {
|
||||||
return false, msg.Methods, nil
|
return false, msg.Methods, nil
|
||||||
case msgUserAuthSuccess:
|
case msgUserAuthSuccess:
|
||||||
return true, nil, nil
|
return true, nil, nil
|
||||||
case msgDisconnect:
|
|
||||||
return false, nil, io.EOF
|
|
||||||
default:
|
default:
|
||||||
return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
|
return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
|
||||||
}
|
}
|
||||||
|
@ -439,3 +437,37 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type retryableAuthMethod struct {
|
||||||
|
authMethod AuthMethod
|
||||||
|
maxTries int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok bool, methods []string, err error) {
|
||||||
|
for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ {
|
||||||
|
ok, methods, err = r.authMethod.auth(session, user, c, rand)
|
||||||
|
if ok || err != nil { // either success or error terminate
|
||||||
|
return ok, methods, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok, methods, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retryableAuthMethod) method() string {
|
||||||
|
return r.authMethod.method()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryableAuthMethod is a decorator for other auth methods enabling them to
|
||||||
|
// be retried up to maxTries before considering that AuthMethod itself failed.
|
||||||
|
// If maxTries is <= 0, will retry indefinitely
|
||||||
|
//
|
||||||
|
// This is useful for interactive clients using challenge/response type
|
||||||
|
// authentication (e.g. Keyboard-Interactive, Password, etc) where the user
|
||||||
|
// could mistype their response resulting in the server issuing a
|
||||||
|
// SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4
|
||||||
|
// [keyboard-interactive]); Without this decorator, the non-retryable
|
||||||
|
// AuthMethod would be removed from future consideration, and never tried again
|
||||||
|
// (and so the user would never be able to retry their entry).
|
||||||
|
func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod {
|
||||||
|
return &retryableAuthMethod{authMethod: auth, maxTries: maxTries}
|
||||||
|
}
|
||||||
|
|
|
@ -44,10 +44,12 @@ var supportedKexAlgos = []string{
|
||||||
// of authenticating servers) in preference order.
|
// of authenticating servers) in preference order.
|
||||||
var supportedHostKeyAlgos = []string{
|
var supportedHostKeyAlgos = []string{
|
||||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
|
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
|
||||||
CertAlgoECDSA384v01, CertAlgoECDSA521v01,
|
CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
|
||||||
|
|
||||||
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
||||||
KeyAlgoRSA, KeyAlgoDSA,
|
KeyAlgoRSA, KeyAlgoDSA,
|
||||||
|
|
||||||
|
KeyAlgoED25519,
|
||||||
}
|
}
|
||||||
|
|
||||||
// supportedMACs specifies a default set of MAC algorithms in preference order.
|
// supportedMACs specifies a default set of MAC algorithms in preference order.
|
||||||
|
|
|
@ -23,7 +23,6 @@ func (e *OpenChannelError) Error() string {
|
||||||
// ConnMetadata holds metadata for the connection.
|
// ConnMetadata holds metadata for the connection.
|
||||||
type ConnMetadata interface {
|
type ConnMetadata interface {
|
||||||
// User returns the user ID for this connection.
|
// User returns the user ID for this connection.
|
||||||
// It is empty if no authentication is used.
|
|
||||||
User() string
|
User() string
|
||||||
|
|
||||||
// SessionID returns the sesson hash, also denoted by H.
|
// SessionID returns the sesson hash, also denoted by H.
|
||||||
|
|
|
@ -29,25 +29,6 @@ type keyingTransport interface {
|
||||||
// direction will be effected if a msgNewKeys message is sent
|
// direction will be effected if a msgNewKeys message is sent
|
||||||
// or received.
|
// or received.
|
||||||
prepareKeyChange(*algorithms, *kexResult) error
|
prepareKeyChange(*algorithms, *kexResult) error
|
||||||
|
|
||||||
// getSessionID returns the session ID. prepareKeyChange must
|
|
||||||
// have been called once.
|
|
||||||
getSessionID() []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// rekeyingTransport is the interface of handshakeTransport that we
|
|
||||||
// (internally) expose to ClientConn and ServerConn.
|
|
||||||
type rekeyingTransport interface {
|
|
||||||
packetConn
|
|
||||||
|
|
||||||
// requestKeyChange asks the remote side to change keys. All
|
|
||||||
// writes are blocked until the key change succeeds, which is
|
|
||||||
// signaled by reading a msgNewKeys.
|
|
||||||
requestKeyChange() error
|
|
||||||
|
|
||||||
// getSessionID returns the session ID. This is only valid
|
|
||||||
// after the first key change has completed.
|
|
||||||
getSessionID() []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handshakeTransport implements rekeying on top of a keyingTransport
|
// handshakeTransport implements rekeying on top of a keyingTransport
|
||||||
|
@ -86,6 +67,9 @@ type handshakeTransport struct {
|
||||||
sentInitMsg *kexInitMsg
|
sentInitMsg *kexInitMsg
|
||||||
writtenSinceKex uint64
|
writtenSinceKex uint64
|
||||||
writeError error
|
writeError error
|
||||||
|
|
||||||
|
// The session ID or nil if first kex did not complete yet.
|
||||||
|
sessionID []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport {
|
func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport {
|
||||||
|
@ -122,7 +106,7 @@ func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *handshakeTransport) getSessionID() []byte {
|
func (t *handshakeTransport) getSessionID() []byte {
|
||||||
return t.conn.getSessionID()
|
return t.sessionID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *handshakeTransport) id() string {
|
func (t *handshakeTransport) id() string {
|
||||||
|
@ -177,15 +161,22 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) {
|
||||||
|
|
||||||
t.readSinceKex += uint64(len(p))
|
t.readSinceKex += uint64(len(p))
|
||||||
if debugHandshake {
|
if debugHandshake {
|
||||||
|
if p[0] == msgChannelData || p[0] == msgChannelExtendedData {
|
||||||
|
log.Printf("%s got data (packet %d bytes)", t.id(), len(p))
|
||||||
|
} else {
|
||||||
msg, err := decode(p)
|
msg, err := decode(p)
|
||||||
log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err)
|
log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if p[0] != msgKexInit {
|
if p[0] != msgKexInit {
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
err = t.enterKeyExchange(p)
|
|
||||||
|
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
|
|
||||||
|
firstKex := t.sessionID == nil
|
||||||
|
|
||||||
|
err = t.enterKeyExchangeLocked(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// drop connection
|
// drop connection
|
||||||
t.conn.Close()
|
t.conn.Close()
|
||||||
|
@ -193,7 +184,7 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if debugHandshake {
|
if debugHandshake {
|
||||||
log.Printf("%s exited key exchange, err %v", t.id(), err)
|
log.Printf("%s exited key exchange (first %v), err %v", t.id(), firstKex, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unblock writers.
|
// Unblock writers.
|
||||||
|
@ -208,28 +199,69 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.readSinceKex = 0
|
t.readSinceKex = 0
|
||||||
return []byte{msgNewKeys}, nil
|
|
||||||
|
// By default, a key exchange is hidden from higher layers by
|
||||||
|
// translating it into msgIgnore.
|
||||||
|
successPacket := []byte{msgIgnore}
|
||||||
|
if firstKex {
|
||||||
|
// sendKexInit() for the first kex waits for
|
||||||
|
// msgNewKeys so the authentication process is
|
||||||
|
// guaranteed to happen over an encrypted transport.
|
||||||
|
successPacket = []byte{msgNewKeys}
|
||||||
|
}
|
||||||
|
|
||||||
|
return successPacket, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// keyChangeCategory describes whether a key exchange is the first on a
|
||||||
|
// connection, or a subsequent one.
|
||||||
|
type keyChangeCategory bool
|
||||||
|
|
||||||
|
const (
|
||||||
|
firstKeyExchange keyChangeCategory = true
|
||||||
|
subsequentKeyExchange keyChangeCategory = false
|
||||||
|
)
|
||||||
|
|
||||||
// sendKexInit sends a key change message, and returns the message
|
// sendKexInit sends a key change message, and returns the message
|
||||||
// that was sent. After initiating the key change, all writes will be
|
// that was sent. After initiating the key change, all writes will be
|
||||||
// blocked until the change is done, and a failed key change will
|
// blocked until the change is done, and a failed key change will
|
||||||
// close the underlying transport. This function is safe for
|
// close the underlying transport. This function is safe for
|
||||||
// concurrent use by multiple goroutines.
|
// concurrent use by multiple goroutines.
|
||||||
func (t *handshakeTransport) sendKexInit() (*kexInitMsg, []byte, error) {
|
func (t *handshakeTransport) sendKexInit(isFirst keyChangeCategory) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
// If this is the initial key change, but we already have a sessionID,
|
||||||
return t.sendKexInitLocked()
|
// then do nothing because the key exchange has already completed
|
||||||
|
// asynchronously.
|
||||||
|
if !isFirst || t.sessionID == nil {
|
||||||
|
_, _, err = t.sendKexInitLocked(isFirst)
|
||||||
|
}
|
||||||
|
t.mu.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if isFirst {
|
||||||
|
if packet, err := t.readPacket(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if packet[0] != msgNewKeys {
|
||||||
|
return unexpectedMessageError(msgNewKeys, packet[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *handshakeTransport) requestInitialKeyChange() error {
|
||||||
|
return t.sendKexInit(firstKeyExchange)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *handshakeTransport) requestKeyChange() error {
|
func (t *handshakeTransport) requestKeyChange() error {
|
||||||
_, _, err := t.sendKexInit()
|
return t.sendKexInit(subsequentKeyExchange)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendKexInitLocked sends a key change message. t.mu must be locked
|
// sendKexInitLocked sends a key change message. t.mu must be locked
|
||||||
// while this happens.
|
// while this happens.
|
||||||
func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) {
|
func (t *handshakeTransport) sendKexInitLocked(isFirst keyChangeCategory) (*kexInitMsg, []byte, error) {
|
||||||
// kexInits may be sent either in response to the other side,
|
// kexInits may be sent either in response to the other side,
|
||||||
// or because our side wants to initiate a key change, so we
|
// or because our side wants to initiate a key change, so we
|
||||||
// may have already sent a kexInit. In that case, don't send a
|
// may have already sent a kexInit. In that case, don't send a
|
||||||
|
@ -237,6 +269,7 @@ func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) {
|
||||||
if t.sentInitMsg != nil {
|
if t.sentInitMsg != nil {
|
||||||
return t.sentInitMsg, t.sentInitPacket, nil
|
return t.sentInitMsg, t.sentInitPacket, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := &kexInitMsg{
|
msg := &kexInitMsg{
|
||||||
KexAlgos: t.config.KeyExchanges,
|
KexAlgos: t.config.KeyExchanges,
|
||||||
CiphersClientServer: t.config.Ciphers,
|
CiphersClientServer: t.config.Ciphers,
|
||||||
|
@ -276,7 +309,7 @@ func (t *handshakeTransport) writePacket(p []byte) error {
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
|
|
||||||
if t.writtenSinceKex > t.config.RekeyThreshold {
|
if t.writtenSinceKex > t.config.RekeyThreshold {
|
||||||
t.sendKexInitLocked()
|
t.sendKexInitLocked(subsequentKeyExchange)
|
||||||
}
|
}
|
||||||
for t.sentInitMsg != nil && t.writeError == nil {
|
for t.sentInitMsg != nil && t.writeError == nil {
|
||||||
t.cond.Wait()
|
t.cond.Wait()
|
||||||
|
@ -300,12 +333,12 @@ func (t *handshakeTransport) Close() error {
|
||||||
return t.conn.Close()
|
return t.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// enterKeyExchange runs the key exchange.
|
// enterKeyExchange runs the key exchange. t.mu must be held while running this.
|
||||||
func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) error {
|
||||||
if debugHandshake {
|
if debugHandshake {
|
||||||
log.Printf("%s entered key exchange", t.id())
|
log.Printf("%s entered key exchange", t.id())
|
||||||
}
|
}
|
||||||
myInit, myInitPacket, err := t.sendKexInit()
|
myInit, myInitPacket, err := t.sendKexInitLocked(subsequentKeyExchange)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -338,7 +371,16 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't send FirstKexFollows, but we handle receiving it.
|
// We don't send FirstKexFollows, but we handle receiving it.
|
||||||
if otherInit.FirstKexFollows && algs.kex != otherInit.KexAlgos[0] {
|
//
|
||||||
|
// RFC 4253 section 7 defines the kex and the agreement method for
|
||||||
|
// first_kex_packet_follows. It states that the guessed packet
|
||||||
|
// should be ignored if the "kex algorithm and/or the host
|
||||||
|
// key algorithm is guessed wrong (server and client have
|
||||||
|
// different preferred algorithm), or if any of the other
|
||||||
|
// algorithms cannot be agreed upon". The other algorithms have
|
||||||
|
// already been checked above so the kex algorithm and host key
|
||||||
|
// algorithm are checked here.
|
||||||
|
if otherInit.FirstKexFollows && (clientInit.KexAlgos[0] != serverInit.KexAlgos[0] || clientInit.ServerHostKeyAlgos[0] != serverInit.ServerHostKeyAlgos[0]) {
|
||||||
// other side sent a kex message for the wrong algorithm,
|
// other side sent a kex message for the wrong algorithm,
|
||||||
// which we have to ignore.
|
// which we have to ignore.
|
||||||
if _, err := t.conn.readPacket(); err != nil {
|
if _, err := t.conn.readPacket(); err != nil {
|
||||||
|
@ -362,6 +404,11 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.sessionID == nil {
|
||||||
|
t.sessionID = result.H
|
||||||
|
}
|
||||||
|
result.SessionID = t.sessionID
|
||||||
|
|
||||||
t.conn.prepareKeyChange(algs, result)
|
t.conn.prepareKeyChange(algs, result)
|
||||||
if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil {
|
if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -371,6 +418,7 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
||||||
} else if packet[0] != msgNewKeys {
|
} else if packet[0] != msgNewKeys {
|
||||||
return unexpectedMessageError(msgNewKeys, packet[0])
|
return unexpectedMessageError(msgNewKeys, packet[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ type kexResult struct {
|
||||||
Hash crypto.Hash
|
Hash crypto.Hash
|
||||||
|
|
||||||
// The session ID, which is the first H computed. This is used
|
// The session ID, which is the first H computed. This is used
|
||||||
// to signal data inside transport.
|
// to derive key material inside the transport.
|
||||||
SessionID []byte
|
SessionID []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ed25519"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These constants represent the algorithm names for key types supported by this
|
// These constants represent the algorithm names for key types supported by this
|
||||||
|
@ -30,6 +32,7 @@ const (
|
||||||
KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
|
KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
|
||||||
KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
|
KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
|
||||||
KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
|
KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
|
||||||
|
KeyAlgoED25519 = "ssh-ed25519"
|
||||||
)
|
)
|
||||||
|
|
||||||
// parsePubKey parses a public key of the given algorithm.
|
// parsePubKey parses a public key of the given algorithm.
|
||||||
|
@ -42,14 +45,16 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
|
||||||
return parseDSA(in)
|
return parseDSA(in)
|
||||||
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
|
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
|
||||||
return parseECDSA(in)
|
return parseECDSA(in)
|
||||||
case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
|
case KeyAlgoED25519:
|
||||||
|
return parseED25519(in)
|
||||||
|
case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
|
||||||
cert, err := parseCert(in, certToPrivAlgo(algo))
|
cert, err := parseCert(in, certToPrivAlgo(algo))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return cert, nil, nil
|
return cert, nil, nil
|
||||||
}
|
}
|
||||||
return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", err)
|
return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
|
// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
|
||||||
|
@ -120,7 +125,7 @@ func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strip out the begining of the known_host key.
|
// Strip out the beginning of the known_host key.
|
||||||
// This is either an optional marker or a (set of) hostname(s).
|
// This is either an optional marker or a (set of) hostname(s).
|
||||||
keyFields := bytes.Fields(in)
|
keyFields := bytes.Fields(in)
|
||||||
if len(keyFields) < 3 || len(keyFields) > 5 {
|
if len(keyFields) < 3 || len(keyFields) > 5 {
|
||||||
|
@ -276,6 +281,12 @@ type PublicKey interface {
|
||||||
Verify(data []byte, sig *Signature) error
|
Verify(data []byte, sig *Signature) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CryptoPublicKey, if implemented by a PublicKey,
|
||||||
|
// returns the underlying crypto.PublicKey form of the key.
|
||||||
|
type CryptoPublicKey interface {
|
||||||
|
CryptoPublicKey() crypto.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
// A Signer can create signatures that verify against a public key.
|
// A Signer can create signatures that verify against a public key.
|
||||||
type Signer interface {
|
type Signer interface {
|
||||||
// PublicKey returns an associated PublicKey instance.
|
// PublicKey returns an associated PublicKey instance.
|
||||||
|
@ -319,6 +330,8 @@ func parseRSA(in []byte) (out PublicKey, rest []byte, err error) {
|
||||||
|
|
||||||
func (r *rsaPublicKey) Marshal() []byte {
|
func (r *rsaPublicKey) Marshal() []byte {
|
||||||
e := new(big.Int).SetInt64(int64(r.E))
|
e := new(big.Int).SetInt64(int64(r.E))
|
||||||
|
// RSA publickey struct layout should match the struct used by
|
||||||
|
// parseRSACert in the x/crypto/ssh/agent package.
|
||||||
wirekey := struct {
|
wirekey := struct {
|
||||||
Name string
|
Name string
|
||||||
E *big.Int
|
E *big.Int
|
||||||
|
@ -341,6 +354,10 @@ func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error {
|
||||||
return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob)
|
return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey {
|
||||||
|
return (*rsa.PublicKey)(r)
|
||||||
|
}
|
||||||
|
|
||||||
type dsaPublicKey dsa.PublicKey
|
type dsaPublicKey dsa.PublicKey
|
||||||
|
|
||||||
func (r *dsaPublicKey) Type() string {
|
func (r *dsaPublicKey) Type() string {
|
||||||
|
@ -369,6 +386,8 @@ func parseDSA(in []byte) (out PublicKey, rest []byte, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *dsaPublicKey) Marshal() []byte {
|
func (k *dsaPublicKey) Marshal() []byte {
|
||||||
|
// DSA publickey struct layout should match the struct used by
|
||||||
|
// parseDSACert in the x/crypto/ssh/agent package.
|
||||||
w := struct {
|
w := struct {
|
||||||
Name string
|
Name string
|
||||||
P, Q, G, Y *big.Int
|
P, Q, G, Y *big.Int
|
||||||
|
@ -407,6 +426,10 @@ func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error {
|
||||||
return errors.New("ssh: signature did not verify")
|
return errors.New("ssh: signature did not verify")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *dsaPublicKey) CryptoPublicKey() crypto.PublicKey {
|
||||||
|
return (*dsa.PublicKey)(k)
|
||||||
|
}
|
||||||
|
|
||||||
type dsaPrivateKey struct {
|
type dsaPrivateKey struct {
|
||||||
*dsa.PrivateKey
|
*dsa.PrivateKey
|
||||||
}
|
}
|
||||||
|
@ -455,6 +478,55 @@ func (key *ecdsaPublicKey) nistID() string {
|
||||||
panic("ssh: unsupported ecdsa key size")
|
panic("ssh: unsupported ecdsa key size")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ed25519PublicKey ed25519.PublicKey
|
||||||
|
|
||||||
|
func (key ed25519PublicKey) Type() string {
|
||||||
|
return KeyAlgoED25519
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseED25519(in []byte) (out PublicKey, rest []byte, err error) {
|
||||||
|
var w struct {
|
||||||
|
KeyBytes []byte
|
||||||
|
Rest []byte `ssh:"rest"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Unmarshal(in, &w); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
key := ed25519.PublicKey(w.KeyBytes)
|
||||||
|
|
||||||
|
return (ed25519PublicKey)(key), w.Rest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key ed25519PublicKey) Marshal() []byte {
|
||||||
|
w := struct {
|
||||||
|
Name string
|
||||||
|
KeyBytes []byte
|
||||||
|
}{
|
||||||
|
KeyAlgoED25519,
|
||||||
|
[]byte(key),
|
||||||
|
}
|
||||||
|
return Marshal(&w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key ed25519PublicKey) Verify(b []byte, sig *Signature) error {
|
||||||
|
if sig.Format != key.Type() {
|
||||||
|
return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
edKey := (ed25519.PublicKey)(key)
|
||||||
|
if ok := ed25519.Verify(edKey, b, sig.Blob); !ok {
|
||||||
|
return errors.New("ssh: signature did not verify")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k ed25519PublicKey) CryptoPublicKey() crypto.PublicKey {
|
||||||
|
return ed25519.PublicKey(k)
|
||||||
|
}
|
||||||
|
|
||||||
func supportedEllipticCurve(curve elliptic.Curve) bool {
|
func supportedEllipticCurve(curve elliptic.Curve) bool {
|
||||||
return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521()
|
return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521()
|
||||||
}
|
}
|
||||||
|
@ -507,6 +579,8 @@ func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) {
|
||||||
func (key *ecdsaPublicKey) Marshal() []byte {
|
func (key *ecdsaPublicKey) Marshal() []byte {
|
||||||
// See RFC 5656, section 3.1.
|
// See RFC 5656, section 3.1.
|
||||||
keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y)
|
keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y)
|
||||||
|
// ECDSA publickey struct layout should match the struct used by
|
||||||
|
// parseECDSACert in the x/crypto/ssh/agent package.
|
||||||
w := struct {
|
w := struct {
|
||||||
Name string
|
Name string
|
||||||
ID string
|
ID string
|
||||||
|
@ -548,6 +622,10 @@ func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error {
|
||||||
return errors.New("ssh: signature did not verify")
|
return errors.New("ssh: signature did not verify")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey {
|
||||||
|
return (*ecdsa.PublicKey)(k)
|
||||||
|
}
|
||||||
|
|
||||||
// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
|
// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
|
||||||
// *ecdsa.PrivateKey or any other crypto.Signer and returns a corresponding
|
// *ecdsa.PrivateKey or any other crypto.Signer and returns a corresponding
|
||||||
// Signer instance. ECDSA keys must use P-256, P-384 or P-521.
|
// Signer instance. ECDSA keys must use P-256, P-384 or P-521.
|
||||||
|
@ -591,13 +669,19 @@ func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
||||||
hashFunc = crypto.SHA1
|
hashFunc = crypto.SHA1
|
||||||
case *ecdsaPublicKey:
|
case *ecdsaPublicKey:
|
||||||
hashFunc = ecHash(key.Curve)
|
hashFunc = ecHash(key.Curve)
|
||||||
|
case ed25519PublicKey:
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var digest []byte
|
||||||
|
if hashFunc != 0 {
|
||||||
h := hashFunc.New()
|
h := hashFunc.New()
|
||||||
h.Write(data)
|
h.Write(data)
|
||||||
digest := h.Sum(nil)
|
digest = h.Sum(nil)
|
||||||
|
} else {
|
||||||
|
digest = data
|
||||||
|
}
|
||||||
|
|
||||||
signature, err := s.signer.Sign(rand, digest, hashFunc)
|
signature, err := s.signer.Sign(rand, digest, hashFunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -637,9 +721,9 @@ func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or
|
// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey,
|
||||||
// any other crypto.Signer and returns a corresponding Signer instance. ECDSA
|
// ed25519.PublicKey, or any other crypto.Signer and returns a corresponding
|
||||||
// keys must use P-256, P-384 or P-521.
|
// Signer instance. ECDSA keys must use P-256, P-384 or P-521.
|
||||||
func NewPublicKey(key interface{}) (PublicKey, error) {
|
func NewPublicKey(key interface{}) (PublicKey, error) {
|
||||||
switch key := key.(type) {
|
switch key := key.(type) {
|
||||||
case *rsa.PublicKey:
|
case *rsa.PublicKey:
|
||||||
|
@ -651,6 +735,8 @@ func NewPublicKey(key interface{}) (PublicKey, error) {
|
||||||
return (*ecdsaPublicKey)(key), nil
|
return (*ecdsaPublicKey)(key), nil
|
||||||
case *dsa.PublicKey:
|
case *dsa.PublicKey:
|
||||||
return (*dsaPublicKey)(key), nil
|
return (*dsaPublicKey)(key), nil
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
return (ed25519PublicKey)(key), nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
||||||
}
|
}
|
||||||
|
@ -667,6 +753,14 @@ func ParsePrivateKey(pemBytes []byte) (Signer, error) {
|
||||||
return NewSignerFromKey(key)
|
return NewSignerFromKey(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// encryptedBlock tells whether a private key is
|
||||||
|
// encrypted by examining its Proc-Type header
|
||||||
|
// for a mention of ENCRYPTED
|
||||||
|
// according to RFC 1421 Section 4.6.1.1.
|
||||||
|
func encryptedBlock(block *pem.Block) bool {
|
||||||
|
return strings.Contains(block.Headers["Proc-Type"], "ENCRYPTED")
|
||||||
|
}
|
||||||
|
|
||||||
// ParseRawPrivateKey returns a private key from a PEM encoded private key. It
|
// ParseRawPrivateKey returns a private key from a PEM encoded private key. It
|
||||||
// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys.
|
// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys.
|
||||||
func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
|
func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
|
||||||
|
@ -675,6 +769,10 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
|
||||||
return nil, errors.New("ssh: no key found")
|
return nil, errors.New("ssh: no key found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if encryptedBlock(block) {
|
||||||
|
return nil, errors.New("ssh: cannot decode encrypted private keys")
|
||||||
|
}
|
||||||
|
|
||||||
switch block.Type {
|
switch block.Type {
|
||||||
case "RSA PRIVATE KEY":
|
case "RSA PRIVATE KEY":
|
||||||
return x509.ParsePKCS1PrivateKey(block.Bytes)
|
return x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
@ -682,6 +780,8 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
|
||||||
return x509.ParseECPrivateKey(block.Bytes)
|
return x509.ParseECPrivateKey(block.Bytes)
|
||||||
case "DSA PRIVATE KEY":
|
case "DSA PRIVATE KEY":
|
||||||
return ParseDSAPrivateKey(block.Bytes)
|
return ParseDSAPrivateKey(block.Bytes)
|
||||||
|
case "OPENSSH PRIVATE KEY":
|
||||||
|
return parseOpenSSHPrivateKey(block.Bytes)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
|
return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
|
||||||
}
|
}
|
||||||
|
@ -718,3 +818,63 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
|
||||||
X: k.Pub,
|
X: k.Pub,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implemented based on the documentation at
|
||||||
|
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
|
||||||
|
func parseOpenSSHPrivateKey(key []byte) (*ed25519.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")
|
||||||
|
}
|
||||||
|
remaining := key[len(magic):]
|
||||||
|
|
||||||
|
var w struct {
|
||||||
|
CipherName string
|
||||||
|
KdfName string
|
||||||
|
KdfOpts string
|
||||||
|
NumKeys uint32
|
||||||
|
PubKey []byte
|
||||||
|
PrivKeyBlock []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Unmarshal(remaining, &w); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pk1 := struct {
|
||||||
|
Check1 uint32
|
||||||
|
Check2 uint32
|
||||||
|
Keytype string
|
||||||
|
Pub []byte
|
||||||
|
Priv []byte
|
||||||
|
Comment string
|
||||||
|
Pad []byte `ssh:"rest"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if pk1.Check1 != pk1.Check2 {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, b := range pk1.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 := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize))
|
||||||
|
copy(pk, pk1.Priv)
|
||||||
|
return &pk, nil
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These are SSH message type numbers. They are scattered around several
|
// These are SSH message type numbers. They are scattered around several
|
||||||
|
@ -47,7 +48,7 @@ type disconnectMsg struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *disconnectMsg) Error() string {
|
func (d *disconnectMsg) Error() string {
|
||||||
return fmt.Sprintf("ssh: disconnect reason %d: %s", d.Reason, d.Message)
|
return fmt.Sprintf("ssh: disconnect, reason %d: %s", d.Reason, d.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// See RFC 4253, section 7.1.
|
// See RFC 4253, section 7.1.
|
||||||
|
@ -124,6 +125,10 @@ type userAuthRequestMsg struct {
|
||||||
Payload []byte `ssh:"rest"`
|
Payload []byte `ssh:"rest"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used for debug printouts of packets.
|
||||||
|
type userAuthSuccessMsg struct {
|
||||||
|
}
|
||||||
|
|
||||||
// See RFC 4252, section 5.1
|
// See RFC 4252, section 5.1
|
||||||
const msgUserAuthFailure = 51
|
const msgUserAuthFailure = 51
|
||||||
|
|
||||||
|
@ -158,6 +163,13 @@ type channelOpenMsg struct {
|
||||||
const msgChannelExtendedData = 95
|
const msgChannelExtendedData = 95
|
||||||
const msgChannelData = 94
|
const msgChannelData = 94
|
||||||
|
|
||||||
|
// Used for debug print outs of packets.
|
||||||
|
type channelDataMsg struct {
|
||||||
|
PeersId uint32 `sshtype:"94"`
|
||||||
|
Length uint32
|
||||||
|
Rest []byte `ssh:"rest"`
|
||||||
|
}
|
||||||
|
|
||||||
// See RFC 4254, section 5.1.
|
// See RFC 4254, section 5.1.
|
||||||
const msgChannelOpenConfirm = 91
|
const msgChannelOpenConfirm = 91
|
||||||
|
|
||||||
|
@ -255,17 +267,19 @@ type userAuthPubKeyOkMsg struct {
|
||||||
PubKey []byte
|
PubKey []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// typeTag returns the type byte for the given type. The type should
|
// typeTags returns the possible type bytes for the given reflect.Type, which
|
||||||
// be struct.
|
// should be a struct. The possible values are separated by a '|' character.
|
||||||
func typeTag(structType reflect.Type) byte {
|
func typeTags(structType reflect.Type) (tags []byte) {
|
||||||
var tag byte
|
tagStr := structType.Field(0).Tag.Get("sshtype")
|
||||||
var tagStr string
|
|
||||||
tagStr = structType.Field(0).Tag.Get("sshtype")
|
for _, tag := range strings.Split(tagStr, "|") {
|
||||||
i, err := strconv.Atoi(tagStr)
|
i, err := strconv.Atoi(tag)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tag = byte(i)
|
tags = append(tags, byte(i))
|
||||||
}
|
}
|
||||||
return tag
|
}
|
||||||
|
|
||||||
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
func fieldError(t reflect.Type, field int, problem string) error {
|
func fieldError(t reflect.Type, field int, problem string) error {
|
||||||
|
@ -279,19 +293,34 @@ var errShortRead = errors.New("ssh: short read")
|
||||||
|
|
||||||
// Unmarshal parses data in SSH wire format into a structure. The out
|
// Unmarshal parses data in SSH wire format into a structure. The out
|
||||||
// argument should be a pointer to struct. If the first member of the
|
// argument should be a pointer to struct. If the first member of the
|
||||||
// struct has the "sshtype" tag set to a number in decimal, the packet
|
// struct has the "sshtype" tag set to a '|'-separated set of numbers
|
||||||
// must start that number. In case of error, Unmarshal returns a
|
// in decimal, the packet must start with one of those numbers. In
|
||||||
// ParseError or UnexpectedMessageError.
|
// case of error, Unmarshal returns a ParseError or
|
||||||
|
// UnexpectedMessageError.
|
||||||
func Unmarshal(data []byte, out interface{}) error {
|
func Unmarshal(data []byte, out interface{}) error {
|
||||||
v := reflect.ValueOf(out).Elem()
|
v := reflect.ValueOf(out).Elem()
|
||||||
structType := v.Type()
|
structType := v.Type()
|
||||||
expectedType := typeTag(structType)
|
expectedTypes := typeTags(structType)
|
||||||
|
|
||||||
|
var expectedType byte
|
||||||
|
if len(expectedTypes) > 0 {
|
||||||
|
expectedType = expectedTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return parseError(expectedType)
|
return parseError(expectedType)
|
||||||
}
|
}
|
||||||
if expectedType > 0 {
|
|
||||||
if data[0] != expectedType {
|
if len(expectedTypes) > 0 {
|
||||||
return unexpectedMessageError(expectedType, data[0])
|
goodType := false
|
||||||
|
for _, e := range expectedTypes {
|
||||||
|
if e > 0 && data[0] == e {
|
||||||
|
goodType = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !goodType {
|
||||||
|
return fmt.Errorf("ssh: unexpected message type %d (expected one of %v)", data[0], expectedTypes)
|
||||||
}
|
}
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
}
|
}
|
||||||
|
@ -375,7 +404,7 @@ func Unmarshal(data []byte, out interface{}) error {
|
||||||
return fieldError(structType, i, "pointer to unsupported type")
|
return fieldError(structType, i, "pointer to unsupported type")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fieldError(structType, i, "unsupported type")
|
return fieldError(structType, i, fmt.Sprintf("unsupported type: %v", t))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,9 +427,9 @@ func Marshal(msg interface{}) []byte {
|
||||||
|
|
||||||
func marshalStruct(out []byte, msg interface{}) []byte {
|
func marshalStruct(out []byte, msg interface{}) []byte {
|
||||||
v := reflect.Indirect(reflect.ValueOf(msg))
|
v := reflect.Indirect(reflect.ValueOf(msg))
|
||||||
msgType := typeTag(v.Type())
|
msgTypes := typeTags(v.Type())
|
||||||
if msgType > 0 {
|
if len(msgTypes) > 0 {
|
||||||
out = append(out, msgType)
|
out = append(out, msgTypes[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, n := 0, v.NumField(); i < n; i++ {
|
for i, n := 0, v.NumField(); i < n; i++ {
|
||||||
|
@ -687,6 +716,8 @@ func decode(packet []byte) (interface{}, error) {
|
||||||
msg = new(kexDHReplyMsg)
|
msg = new(kexDHReplyMsg)
|
||||||
case msgUserAuthRequest:
|
case msgUserAuthRequest:
|
||||||
msg = new(userAuthRequestMsg)
|
msg = new(userAuthRequestMsg)
|
||||||
|
case msgUserAuthSuccess:
|
||||||
|
return new(userAuthSuccessMsg), nil
|
||||||
case msgUserAuthFailure:
|
case msgUserAuthFailure:
|
||||||
msg = new(userAuthFailureMsg)
|
msg = new(userAuthFailureMsg)
|
||||||
case msgUserAuthPubKeyOk:
|
case msgUserAuthPubKeyOk:
|
||||||
|
@ -699,6 +730,8 @@ func decode(packet []byte) (interface{}, error) {
|
||||||
msg = new(globalRequestFailureMsg)
|
msg = new(globalRequestFailureMsg)
|
||||||
case msgChannelOpen:
|
case msgChannelOpen:
|
||||||
msg = new(channelOpenMsg)
|
msg = new(channelOpenMsg)
|
||||||
|
case msgChannelData:
|
||||||
|
msg = new(channelDataMsg)
|
||||||
case msgChannelOpenConfirm:
|
case msgChannelOpenConfirm:
|
||||||
msg = new(channelOpenConfirmMsg)
|
msg = new(channelOpenConfirmMsg)
|
||||||
case msgChannelOpenFailure:
|
case msgChannelOpenFailure:
|
||||||
|
|
|
@ -131,6 +131,9 @@ func newMux(p packetConn) *mux {
|
||||||
|
|
||||||
func (m *mux) sendMessage(msg interface{}) error {
|
func (m *mux) sendMessage(msg interface{}) error {
|
||||||
p := Marshal(msg)
|
p := Marshal(msg)
|
||||||
|
if debugMux {
|
||||||
|
log.Printf("send global(%d): %#v", m.chanList.offset, msg)
|
||||||
|
}
|
||||||
return m.conn.writePacket(p)
|
return m.conn.writePacket(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,18 +178,6 @@ func (m *mux) ackRequest(ok bool, data []byte) error {
|
||||||
return m.sendMessage(globalRequestFailureMsg{Data: data})
|
return m.sendMessage(globalRequestFailureMsg{Data: data})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(hanwen): Disconnect is a transport layer message. We should
|
|
||||||
// probably send and receive Disconnect somewhere in the transport
|
|
||||||
// code.
|
|
||||||
|
|
||||||
// Disconnect sends a disconnect message.
|
|
||||||
func (m *mux) Disconnect(reason uint32, message string) error {
|
|
||||||
return m.sendMessage(disconnectMsg{
|
|
||||||
Reason: reason,
|
|
||||||
Message: message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mux) Close() error {
|
func (m *mux) Close() error {
|
||||||
return m.conn.Close()
|
return m.conn.Close()
|
||||||
}
|
}
|
||||||
|
@ -236,11 +227,6 @@ func (m *mux) onePacket() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch packet[0] {
|
switch packet[0] {
|
||||||
case msgNewKeys:
|
|
||||||
// Ignore notification of key change.
|
|
||||||
return nil
|
|
||||||
case msgDisconnect:
|
|
||||||
return m.handleDisconnect(packet)
|
|
||||||
case msgChannelOpen:
|
case msgChannelOpen:
|
||||||
return m.handleChannelOpen(packet)
|
return m.handleChannelOpen(packet)
|
||||||
case msgGlobalRequest, msgRequestSuccess, msgRequestFailure:
|
case msgGlobalRequest, msgRequestSuccess, msgRequestFailure:
|
||||||
|
@ -260,18 +246,6 @@ func (m *mux) onePacket() error {
|
||||||
return ch.handlePacket(packet)
|
return ch.handlePacket(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mux) handleDisconnect(packet []byte) error {
|
|
||||||
var d disconnectMsg
|
|
||||||
if err := Unmarshal(packet, &d); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if debugMux {
|
|
||||||
log.Printf("caught disconnect: %v", d)
|
|
||||||
}
|
|
||||||
return &d
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mux) handleGlobalPacket(packet []byte) error {
|
func (m *mux) handleGlobalPacket(packet []byte) error {
|
||||||
msg, err := decode(packet)
|
msg, err := decode(packet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -188,16 +188,10 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
|
||||||
tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
|
tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
|
||||||
s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config)
|
s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config)
|
||||||
|
|
||||||
if err := s.transport.requestKeyChange(); err != nil {
|
if err := s.transport.requestInitialKeyChange(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if packet, err := s.transport.readPacket(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if packet[0] != msgNewKeys {
|
|
||||||
return nil, unexpectedMessageError(msgNewKeys, packet[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// We just did the key change, so the session ID is established.
|
// We just did the key change, so the session ID is established.
|
||||||
s.sessionID = s.transport.getSessionID()
|
s.sessionID = s.transport.getSessionID()
|
||||||
|
|
||||||
|
@ -230,7 +224,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
|
||||||
|
|
||||||
func isAcceptableAlgo(algo string) bool {
|
func isAcceptableAlgo(algo string) bool {
|
||||||
switch algo {
|
switch algo {
|
||||||
case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519,
|
||||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
|
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -290,7 +284,6 @@ userAuthLoop:
|
||||||
switch userAuthReq.Method {
|
switch userAuthReq.Method {
|
||||||
case "none":
|
case "none":
|
||||||
if config.NoClientAuth {
|
if config.NoClientAuth {
|
||||||
s.user = ""
|
|
||||||
authErr = nil
|
authErr = nil
|
||||||
}
|
}
|
||||||
case "password":
|
case "password":
|
||||||
|
|
|
@ -9,6 +9,7 @@ package ssh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -281,9 +282,10 @@ func (s *Session) Start(cmd string) error {
|
||||||
// copying stdin, stdout, and stderr, and exits with a zero exit
|
// copying stdin, stdout, and stderr, and exits with a zero exit
|
||||||
// status.
|
// status.
|
||||||
//
|
//
|
||||||
// If the command fails to run or doesn't complete successfully, the
|
// If the remote server does not send an exit status, an error of type
|
||||||
// error is of type *ExitError. Other error types may be
|
// *ExitMissingError is returned. If the command completes
|
||||||
// returned for I/O problems.
|
// unsuccessfully or is interrupted by a signal, the error is of type
|
||||||
|
// *ExitError. Other error types may be returned for I/O problems.
|
||||||
func (s *Session) Run(cmd string) error {
|
func (s *Session) Run(cmd string) error {
|
||||||
err := s.Start(cmd)
|
err := s.Start(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -370,9 +372,10 @@ func (s *Session) start() error {
|
||||||
// copying stdin, stdout, and stderr, and exits with a zero exit
|
// copying stdin, stdout, and stderr, and exits with a zero exit
|
||||||
// status.
|
// status.
|
||||||
//
|
//
|
||||||
// If the command fails to run or doesn't complete successfully, the
|
// If the remote server does not send an exit status, an error of type
|
||||||
// error is of type *ExitError. Other error types may be
|
// *ExitMissingError is returned. If the command completes
|
||||||
// returned for I/O problems.
|
// unsuccessfully or is interrupted by a signal, the error is of type
|
||||||
|
// *ExitError. Other error types may be returned for I/O problems.
|
||||||
func (s *Session) Wait() error {
|
func (s *Session) Wait() error {
|
||||||
if !s.started {
|
if !s.started {
|
||||||
return errors.New("ssh: session not started")
|
return errors.New("ssh: session not started")
|
||||||
|
@ -400,8 +403,7 @@ func (s *Session) wait(reqs <-chan *Request) error {
|
||||||
for msg := range reqs {
|
for msg := range reqs {
|
||||||
switch msg.Type {
|
switch msg.Type {
|
||||||
case "exit-status":
|
case "exit-status":
|
||||||
d := msg.Payload
|
wm.status = int(binary.BigEndian.Uint32(msg.Payload))
|
||||||
wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3])
|
|
||||||
case "exit-signal":
|
case "exit-signal":
|
||||||
var sigval struct {
|
var sigval struct {
|
||||||
Signal string
|
Signal string
|
||||||
|
@ -431,16 +433,29 @@ func (s *Session) wait(reqs <-chan *Request) error {
|
||||||
if wm.status == -1 {
|
if wm.status == -1 {
|
||||||
// exit-status was never sent from server
|
// exit-status was never sent from server
|
||||||
if wm.signal == "" {
|
if wm.signal == "" {
|
||||||
return errors.New("wait: remote command exited without exit status or exit signal")
|
// signal was not sent either. RFC 4254
|
||||||
|
// section 6.10 recommends against this
|
||||||
|
// behavior, but it is allowed, so we let
|
||||||
|
// clients handle it.
|
||||||
|
return &ExitMissingError{}
|
||||||
}
|
}
|
||||||
wm.status = 128
|
wm.status = 128
|
||||||
if _, ok := signals[Signal(wm.signal)]; ok {
|
if _, ok := signals[Signal(wm.signal)]; ok {
|
||||||
wm.status += signals[Signal(wm.signal)]
|
wm.status += signals[Signal(wm.signal)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ExitError{wm}
|
return &ExitError{wm}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExitMissingError is returned if a session is torn down cleanly, but
|
||||||
|
// the server sends no confirmation of the exit status.
|
||||||
|
type ExitMissingError struct{}
|
||||||
|
|
||||||
|
func (e *ExitMissingError) Error() string {
|
||||||
|
return "wait: remote command exited without exit status or exit signal"
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Session) stdin() {
|
func (s *Session) stdin() {
|
||||||
if s.stdinpipe {
|
if s.stdinpipe {
|
||||||
return
|
return
|
||||||
|
@ -601,5 +616,12 @@ func (w Waitmsg) Lang() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w Waitmsg) String() string {
|
func (w Waitmsg) String() string {
|
||||||
return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.status, w.msg, w.signal)
|
str := fmt.Sprintf("Process exited with status %v", w.status)
|
||||||
|
if w.signal != "" {
|
||||||
|
str += fmt.Sprintf(" from signal %v", w.signal)
|
||||||
|
}
|
||||||
|
if w.msg != "" {
|
||||||
|
str += fmt.Sprintf(". Reason was: %v", w.msg)
|
||||||
|
}
|
||||||
|
return str
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,13 @@ func MakeRaw(fd int) (*State, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
newState := oldState.termios
|
newState := oldState.termios
|
||||||
newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF
|
// This attempts to replicate the behaviour documented for cfmakeraw in
|
||||||
newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG
|
// the termios(3) manpage.
|
||||||
|
newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
|
||||||
|
newState.Oflag &^= syscall.OPOST
|
||||||
|
newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
|
||||||
|
newState.Cflag &^= syscall.CSIZE | syscall.PARENB
|
||||||
|
newState.Cflag |= syscall.CS8
|
||||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright 2016 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 terminal provides support functions for dealing with terminals, as
|
||||||
|
// commonly found on UNIX systems.
|
||||||
|
//
|
||||||
|
// Putting a terminal into raw mode is the most common requirement:
|
||||||
|
//
|
||||||
|
// oldState, err := terminal.MakeRaw(0)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// defer terminal.Restore(0, oldState)
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type State struct{}
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal(fd int) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw put 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.
|
||||||
|
func MakeRaw(fd int) (*State, error) {
|
||||||
|
return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore restores the terminal connected to the given file descriptor to a
|
||||||
|
// previous state.
|
||||||
|
func Restore(fd int, state *State) error {
|
||||||
|
return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSize returns the dimensions of the given terminal.
|
||||||
|
func GetSize(fd int) (width, height int, err error) {
|
||||||
|
return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||||
|
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||||
|
// returned does not include the \n.
|
||||||
|
func ReadPassword(fd int) ([]byte, error) {
|
||||||
|
return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright 2015 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 solaris
|
||||||
|
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State contains the state of a terminal.
|
||||||
|
type State struct {
|
||||||
|
termios syscall.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)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||||
|
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||||
|
// returned does not include the \n.
|
||||||
|
func ReadPassword(fd int) ([]byte, error) {
|
||||||
|
// see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
|
||||||
|
val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
oldState := *val
|
||||||
|
|
||||||
|
newState := oldState
|
||||||
|
newState.Lflag &^= syscall.ECHO
|
||||||
|
newState.Lflag |= syscall.ICANON | syscall.ISIG
|
||||||
|
newState.Iflag |= syscall.ICRNL
|
||||||
|
err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)
|
||||||
|
|
||||||
|
var buf [16]byte
|
||||||
|
var ret []byte
|
||||||
|
for {
|
||||||
|
n, err := syscall.Read(fd, buf[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
if len(ret) == 0 {
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if buf[n-1] == '\n' {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
ret = append(ret, buf[:n]...)
|
||||||
|
if n < len(buf) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
|
@ -87,8 +87,8 @@ func MakeRaw(fd int) (*State, error) {
|
||||||
if e != 0 {
|
if e != 0 {
|
||||||
return nil, error(e)
|
return nil, error(e)
|
||||||
}
|
}
|
||||||
st &^= (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
|
raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
|
||||||
_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0)
|
_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0)
|
||||||
if e != 0 {
|
if e != 0 {
|
||||||
return nil, error(e)
|
return nil, error(e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
const (
|
const (
|
||||||
gcmCipherID = "aes128-gcm@openssh.com"
|
gcmCipherID = "aes128-gcm@openssh.com"
|
||||||
aes128cbcID = "aes128-cbc"
|
aes128cbcID = "aes128-cbc"
|
||||||
|
tripledescbcID = "3des-cbc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// packetConn represents a transport that implements packet based
|
// packetConn represents a transport that implements packet based
|
||||||
|
@ -39,19 +40,6 @@ type transport struct {
|
||||||
rand io.Reader
|
rand io.Reader
|
||||||
|
|
||||||
io.Closer
|
io.Closer
|
||||||
|
|
||||||
// Initial H used for the session ID. Once assigned this does
|
|
||||||
// not change, even during subsequent key exchanges.
|
|
||||||
sessionID []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// getSessionID returns the ID of the SSH connection. The return value
|
|
||||||
// should not be modified.
|
|
||||||
func (t *transport) getSessionID() []byte {
|
|
||||||
if t.sessionID == nil {
|
|
||||||
panic("session ID not set yet")
|
|
||||||
}
|
|
||||||
return t.sessionID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// packetCipher represents a combination of SSH encryption/MAC
|
// packetCipher represents a combination of SSH encryption/MAC
|
||||||
|
@ -81,12 +69,6 @@ type connectionState struct {
|
||||||
// both directions are triggered by reading and writing a msgNewKey packet
|
// both directions are triggered by reading and writing a msgNewKey packet
|
||||||
// respectively.
|
// respectively.
|
||||||
func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
|
func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
|
||||||
if t.sessionID == nil {
|
|
||||||
t.sessionID = kexResult.H
|
|
||||||
}
|
|
||||||
|
|
||||||
kexResult.SessionID = t.sessionID
|
|
||||||
|
|
||||||
if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil {
|
if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
|
@ -114,13 +96,28 @@ func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
|
||||||
err = errors.New("ssh: zero length packet")
|
err = errors.New("ssh: zero length packet")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(packet) > 0 && packet[0] == msgNewKeys {
|
if len(packet) > 0 {
|
||||||
|
switch packet[0] {
|
||||||
|
case msgNewKeys:
|
||||||
select {
|
select {
|
||||||
case cipher := <-s.pendingKeyChange:
|
case cipher := <-s.pendingKeyChange:
|
||||||
s.packetCipher = cipher
|
s.packetCipher = cipher
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("ssh: got bogus newkeys message.")
|
return nil, errors.New("ssh: got bogus newkeys message.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case msgDisconnect:
|
||||||
|
// Transform a disconnect message into an
|
||||||
|
// error. Since this is lowest level at which
|
||||||
|
// we interpret message types, doing it here
|
||||||
|
// ensures that we don't have to handle it
|
||||||
|
// elsewhere.
|
||||||
|
var msg disconnectMsg
|
||||||
|
if err := Unmarshal(packet, &msg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, &msg
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The packet may point to an internal buffer, so copy the
|
// The packet may point to an internal buffer, so copy the
|
||||||
|
@ -223,6 +220,10 @@ func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (pac
|
||||||
return newAESCBCCipher(iv, key, macKey, algs)
|
return newAESCBCCipher(iv, key, macKey, algs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if algs.Cipher == tripledescbcID {
|
||||||
|
return newTripleDESCBCCipher(iv, key, macKey, algs)
|
||||||
|
}
|
||||||
|
|
||||||
c := &streamPacketCipher{
|
c := &streamPacketCipher{
|
||||||
mac: macModes[algs.MAC].new(macKey),
|
mac: macModes[algs.MAC].new(macKey),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue