go.crypto/ssh: remove old files.
In c0fc595a2cb5, hg didn't notice the files that had been removed from the working directory. These, old files are breaking the build. CC=golang-codereviews https://golang.org/cl/86240043
This commit is contained in:
-250
@@ -1,250 +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.
|
||||
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// See [PROTOCOL.agent], section 3.
|
||||
const (
|
||||
// 3.2 Requests from client to agent for protocol 2 key operations
|
||||
agentRequestIdentities = 11
|
||||
agentSignRequest = 13
|
||||
agentAddIdentity = 17
|
||||
agentRemoveIdentity = 18
|
||||
agentRemoveAllIdentities = 19
|
||||
agentAddIdConstrained = 25
|
||||
|
||||
// 3.3 Key-type independent requests from client to agent
|
||||
agentAddSmartcardKey = 20
|
||||
agentRemoveSmartcardKey = 21
|
||||
agentLock = 22
|
||||
agentUnlock = 23
|
||||
agentAddSmartcardKeyConstrained = 26
|
||||
|
||||
// 3.4 Generic replies from agent to client
|
||||
agentFailure = 5
|
||||
agentSuccess = 6
|
||||
|
||||
// 3.6 Replies from agent to client for protocol 2 key operations
|
||||
agentIdentitiesAnswer = 12
|
||||
agentSignResponse = 14
|
||||
|
||||
// 3.7 Key constraint identifiers
|
||||
agentConstrainLifetime = 1
|
||||
agentConstrainConfirm = 2
|
||||
)
|
||||
|
||||
// maxAgentResponseBytes is the maximum agent reply size that is accepted. This
|
||||
// is a sanity check, not a limit in the spec.
|
||||
const maxAgentResponseBytes = 16 << 20
|
||||
|
||||
// Agent messages:
|
||||
// These structures mirror the wire format of the corresponding ssh agent
|
||||
// messages found in [PROTOCOL.agent].
|
||||
|
||||
type failureAgentMsg struct{}
|
||||
|
||||
type successAgentMsg struct{}
|
||||
|
||||
// See [PROTOCOL.agent], section 2.5.2.
|
||||
type requestIdentitiesAgentMsg struct{}
|
||||
|
||||
// See [PROTOCOL.agent], section 2.5.2.
|
||||
type identitiesAnswerAgentMsg struct {
|
||||
NumKeys uint32
|
||||
Keys []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
// See [PROTOCOL.agent], section 2.6.2.
|
||||
type signRequestAgentMsg struct {
|
||||
KeyBlob []byte
|
||||
Data []byte
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
// See [PROTOCOL.agent], section 2.6.2.
|
||||
type signResponseAgentMsg struct {
|
||||
SigBlob []byte
|
||||
}
|
||||
|
||||
// AgentKey represents a protocol 2 key as defined in [PROTOCOL.agent],
|
||||
// section 2.5.2.
|
||||
type AgentKey struct {
|
||||
blob []byte
|
||||
Comment string
|
||||
}
|
||||
|
||||
// String returns the storage form of an agent key with the format, base64
|
||||
// encoded serialized key, and the comment if it is not empty.
|
||||
func (ak *AgentKey) String() string {
|
||||
algo, _, ok := parseString(ak.blob)
|
||||
if !ok {
|
||||
return "ssh: malformed key"
|
||||
}
|
||||
|
||||
s := string(algo) + " " + base64.StdEncoding.EncodeToString(ak.blob)
|
||||
|
||||
if ak.Comment != "" {
|
||||
s += " " + ak.Comment
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Key returns an agent's public key as one of the supported key or certificate types.
|
||||
func (ak *AgentKey) Key() (PublicKey, error) {
|
||||
if key, _, ok := ParsePublicKey(ak.blob); ok {
|
||||
return key, nil
|
||||
}
|
||||
return nil, errors.New("ssh: failed to parse key blob")
|
||||
}
|
||||
|
||||
func parseAgentKey(in []byte) (out *AgentKey, rest []byte, ok bool) {
|
||||
ak := new(AgentKey)
|
||||
|
||||
if ak.blob, in, ok = parseString(in); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
comment, in, ok := parseString(in)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ak.Comment = string(comment)
|
||||
|
||||
return ak, in, true
|
||||
}
|
||||
|
||||
// AgentClient provides a means to communicate with an ssh agent process based
|
||||
// on the protocol described in [PROTOCOL.agent]?rev=1.6.
|
||||
type AgentClient struct {
|
||||
// conn is typically represented by using a *net.UnixConn
|
||||
conn io.ReadWriter
|
||||
// mu is used to prevent concurrent access to the agent
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewAgentClient creates and returns a new *AgentClient using the
|
||||
// passed in io.ReadWriter as a connection to a ssh agent.
|
||||
func NewAgentClient(rw io.ReadWriter) *AgentClient {
|
||||
return &AgentClient{conn: rw}
|
||||
}
|
||||
|
||||
// sendAndReceive sends req to the agent and waits for a reply. On success,
|
||||
// the reply is unmarshaled into reply and replyType is set to the first byte of
|
||||
// the reply, which contains the type of the message.
|
||||
func (ac *AgentClient) sendAndReceive(req []byte) (reply interface{}, replyType uint8, err error) {
|
||||
// ac.mu prevents multiple, concurrent requests. Since the agent is typically
|
||||
// on the same machine, we don't attempt to pipeline the requests.
|
||||
ac.mu.Lock()
|
||||
defer ac.mu.Unlock()
|
||||
|
||||
msg := make([]byte, stringLength(len(req)))
|
||||
marshalString(msg, req)
|
||||
if _, err = ac.conn.Write(msg); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var respSizeBuf [4]byte
|
||||
if _, err = io.ReadFull(ac.conn, respSizeBuf[:]); err != nil {
|
||||
return
|
||||
}
|
||||
respSize, _, _ := parseUint32(respSizeBuf[:])
|
||||
|
||||
if respSize > maxAgentResponseBytes {
|
||||
err = errors.New("ssh: agent reply too large")
|
||||
return
|
||||
}
|
||||
|
||||
buf := make([]byte, respSize)
|
||||
if _, err = io.ReadFull(ac.conn, buf); err != nil {
|
||||
return
|
||||
}
|
||||
return unmarshalAgentMsg(buf)
|
||||
}
|
||||
|
||||
// RequestIdentities queries the agent for protocol 2 keys as defined in
|
||||
// [PROTOCOL.agent] section 2.5.2.
|
||||
func (ac *AgentClient) RequestIdentities() ([]*AgentKey, error) {
|
||||
req := marshal(agentRequestIdentities, requestIdentitiesAgentMsg{})
|
||||
|
||||
msg, msgType, err := ac.sendAndReceive(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case *identitiesAnswerAgentMsg:
|
||||
if msg.NumKeys > maxAgentResponseBytes/8 {
|
||||
return nil, errors.New("ssh: too many keys in agent reply")
|
||||
}
|
||||
keys := make([]*AgentKey, msg.NumKeys)
|
||||
data := msg.Keys
|
||||
for i := uint32(0); i < msg.NumKeys; i++ {
|
||||
var key *AgentKey
|
||||
var ok bool
|
||||
if key, data, ok = parseAgentKey(data); !ok {
|
||||
return nil, ParseError{agentIdentitiesAnswer}
|
||||
}
|
||||
keys[i] = key
|
||||
}
|
||||
return keys, nil
|
||||
case *failureAgentMsg:
|
||||
return nil, errors.New("ssh: failed to list keys")
|
||||
}
|
||||
return nil, UnexpectedMessageError{agentIdentitiesAnswer, msgType}
|
||||
}
|
||||
|
||||
// SignRequest requests the signing of data by the agent using a protocol 2 key
|
||||
// as defined in [PROTOCOL.agent] section 2.6.2.
|
||||
func (ac *AgentClient) SignRequest(key PublicKey, data []byte) ([]byte, error) {
|
||||
req := marshal(agentSignRequest, signRequestAgentMsg{
|
||||
KeyBlob: MarshalPublicKey(key),
|
||||
Data: data,
|
||||
})
|
||||
|
||||
msg, msgType, err := ac.sendAndReceive(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case *signResponseAgentMsg:
|
||||
return msg.SigBlob, nil
|
||||
case *failureAgentMsg:
|
||||
return nil, errors.New("ssh: failed to sign challenge")
|
||||
}
|
||||
return nil, UnexpectedMessageError{agentSignResponse, msgType}
|
||||
}
|
||||
|
||||
// unmarshalAgentMsg parses an agent message in packet, returning the parsed
|
||||
// form and the message type of packet.
|
||||
func unmarshalAgentMsg(packet []byte) (interface{}, uint8, error) {
|
||||
if len(packet) < 1 {
|
||||
return nil, 0, ParseError{0}
|
||||
}
|
||||
var msg interface{}
|
||||
switch packet[0] {
|
||||
case agentFailure:
|
||||
msg = new(failureAgentMsg)
|
||||
case agentSuccess:
|
||||
msg = new(successAgentMsg)
|
||||
case agentIdentitiesAnswer:
|
||||
msg = new(identitiesAnswerAgentMsg)
|
||||
case agentSignResponse:
|
||||
msg = new(signResponseAgentMsg)
|
||||
default:
|
||||
return nil, 0, UnexpectedMessageError{0, packet[0]}
|
||||
}
|
||||
if err := unmarshal(msg, packet, packet[0]); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return msg, packet[0], nil
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSafeString(t *testing.T) {
|
||||
strings := map[string]string{
|
||||
"\x20\x0d\x0a": "\x20\x0d\x0a",
|
||||
"flibble": "flibble",
|
||||
"new\x20line": "new\x20line",
|
||||
"123456\x07789": "123456 789",
|
||||
"\t\t\x10\r\n": "\t\t \r\n",
|
||||
}
|
||||
|
||||
for s, expected := range strings {
|
||||
actual := safeString(s)
|
||||
if expected != actual {
|
||||
t.Errorf("expected: %v, actual: %v", []byte(expected), []byte(actual))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure Read/Write are not exposed.
|
||||
func TestConnHideRWMethods(t *testing.T) {
|
||||
for _, c := range []interface{}{new(ServerConn), new(ClientConn)} {
|
||||
if _, ok := c.(io.Reader); ok {
|
||||
t.Errorf("%T implements io.Reader", c)
|
||||
}
|
||||
if _, ok := c.(io.Writer); ok {
|
||||
t.Errorf("%T implements io.Writer", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnSupportsLocalRemoteMethods(t *testing.T) {
|
||||
type LocalAddr interface {
|
||||
LocalAddr() net.Addr
|
||||
}
|
||||
type RemoteAddr interface {
|
||||
RemoteAddr() net.Addr
|
||||
}
|
||||
for _, c := range []interface{}{new(ServerConn), new(ClientConn)} {
|
||||
if _, ok := c.(LocalAddr); !ok {
|
||||
t.Errorf("%T does not implement LocalAddr", c)
|
||||
}
|
||||
if _, ok := c.(RemoteAddr); !ok {
|
||||
t.Errorf("%T does not implement RemoteAddr", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ssh
|
||||
|
||||
// A Terminal is capable of parsing and generating virtual terminal
|
||||
// data from an SSH client.
|
||||
type Terminal interface {
|
||||
ReadLine() (line string, err error)
|
||||
SetSize(x, y int)
|
||||
Write([]byte) (int, error)
|
||||
}
|
||||
|
||||
// ServerTerminal contains the state for running a terminal that is capable of
|
||||
// reading lines of input.
|
||||
type ServerTerminal struct {
|
||||
Term Terminal
|
||||
Channel Channel
|
||||
}
|
||||
|
||||
// parsePtyRequest parses the payload of the pty-req message and extracts the
|
||||
// dimensions of the terminal. See RFC 4254, section 6.2.
|
||||
func parsePtyRequest(s []byte) (width, height int, ok bool) {
|
||||
_, s, ok = parseString(s)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
width32, s, ok := parseUint32(s)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
height32, _, ok := parseUint32(s)
|
||||
width = int(width32)
|
||||
height = int(height32)
|
||||
if width < 1 {
|
||||
ok = false
|
||||
}
|
||||
if height < 1 {
|
||||
ok = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ss *ServerTerminal) Write(buf []byte) (n int, err error) {
|
||||
return ss.Term.Write(buf)
|
||||
}
|
||||
|
||||
// ReadLine returns a line of input from the terminal.
|
||||
func (ss *ServerTerminal) ReadLine() (line string, err error) {
|
||||
for {
|
||||
if line, err = ss.Term.ReadLine(); err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
req, ok := err.(ChannelRequest)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
ok = false
|
||||
switch req.Request {
|
||||
case "pty-req":
|
||||
var width, height int
|
||||
width, height, ok = parsePtyRequest(req.Payload)
|
||||
ss.Term.SetSize(width, height)
|
||||
case "shell":
|
||||
ok = true
|
||||
if len(req.Payload) > 0 {
|
||||
// We don't accept any commands, only the default shell.
|
||||
ok = false
|
||||
}
|
||||
case "env":
|
||||
ok = true
|
||||
}
|
||||
if req.WantReply {
|
||||
ss.Channel.AckRequest(ok)
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.google.com/p/go.crypto/ssh"
|
||||
)
|
||||
|
||||
var (
|
||||
validKey = `AAAAB3NzaC1yc2EAAAADAQABAAABAQDEX/dPu4PmtvgK3La9zioCEDrJ` +
|
||||
`yUr6xEIK7Pr+rLgydcqWTU/kt7w7gKjOw4vvzgHfjKl09CWyvgb+y5dCiTk` +
|
||||
`9MxI+erGNhs3pwaoS+EavAbawB7iEqYyTep3YaJK+4RJ4OX7ZlXMAIMrTL+` +
|
||||
`UVrK89t56hCkFYaAgo3VY+z6rb/b3bDBYtE1Y2tS7C3au73aDgeb9psIrSV` +
|
||||
`86ucKBTl5X62FnYiyGd++xCnLB6uLximM5OKXfLzJQNS/QyZyk12g3D8y69` +
|
||||
`Xw1GzCSKX1u1+MQboyf0HJcG2ryUCLHdcDVppApyHx2OLq53hlkQ/yxdflD` +
|
||||
`qCqAE4j+doagSsIfC1T2T`
|
||||
|
||||
authWithOptions = []string{
|
||||
`# comments to ignore before any keys...`,
|
||||
``,
|
||||
`env="HOME=/home/root",no-port-forwarding ssh-rsa ` + validKey + ` user@host`,
|
||||
`# comments to ignore, along with a blank line`,
|
||||
``,
|
||||
`env="HOME=/home/root2" ssh-rsa ` + validKey + ` user2@host2`,
|
||||
``,
|
||||
`# more comments, plus a invalid entry`,
|
||||
`ssh-rsa data-that-will-not-parse user@host3`,
|
||||
}
|
||||
|
||||
authOptions = strings.Join(authWithOptions, "\n")
|
||||
authWithCRLF = strings.Join(authWithOptions, "\r\n")
|
||||
authInvalid = []byte(`ssh-rsa`)
|
||||
authWithQuotedCommaInEnv = []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + validKey + ` user@host`)
|
||||
authWithQuotedSpaceInEnv = []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + validKey + ` user@host`)
|
||||
authWithQuotedQuoteInEnv = []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + validKey + ` user@host`)
|
||||
|
||||
authWithDoubleQuotedQuote = []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + validKey + "\t" + `user@host`)
|
||||
authWithInvalidSpace = []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + validKey + ` user@host
|
||||
#more to follow but still no valid keys`)
|
||||
authWithMissingQuote = []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + validKey + ` user@host
|
||||
env="HOME=/home/root",shared-control ssh-rsa ` + validKey + ` user@host`)
|
||||
|
||||
testClientPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAxF/3T7uD5rb4Cty2vc4qAhA6yclK+sRCCuz6/qy4MnXKlk1P
|
||||
5Le8O4CozsOL784B34ypdPQlsr4G/suXQok5PTMSPnqxjYbN6cGqEvhGrwG2sAe4
|
||||
hKmMk3qd2GiSvuESeDl+2ZVzACDK0y/lFayvPbeeoQpBWGgIKN1WPs+q2/292wwW
|
||||
LRNWNrUuwt2ru92g4Hm/abCK0lfOrnCgU5eV+thZ2IshnfvsQpyweri8YpjOTil3
|
||||
y8yUDUv0MmcpNdoNw/MuvV8NRswkil9btfjEG6Mn9ByXBtq8lAix3XA1aaQKch8d
|
||||
ji6ud4ZZEP8sXX5Q6gqgBOI/naGoErCHwtU9kwIDAQABAoIBAFJRKAp0QEZmTHPB
|
||||
MZk+4r0asIoFpziXLFgIHu7C2DPOzK1Umzj1DCKlPB3wOqi7Ym2jOSWdcnAK2EPW
|
||||
dAGgJC5TSkKGjAcXixmB5RkumfKidUI0+lQh/puTurcMnvcEwglDkLkEvMBA/sSo
|
||||
Pw9m486rOgOnmNzGPyViItURmD2+0yDdLl/vOsO/L1p76GCd0q0J3LqnmsQmawi7
|
||||
Zwj2Stm6BIrggG5GsF204Iet5219TYLo4g1Qb2AlJ9C8P1FtAWhMwJalDxH9Os2/
|
||||
KCDjnaq5n3bXbIU+3QjskjeVXL/Fnbhjnh4zs1EA7eHzl9dCGbcZ2LOimo2PRo8q
|
||||
wVQmz4ECgYEA9dhiu74TxRVoaO5N2X+FsMzRO8gZdP3Z9IrV4jVN8WT4Vdp0snoF
|
||||
gkVkqqbQUNKUb5K6B3Js/qNKfcjLbCNq9fewTcT6WsHQdtPbX/QA6Pa2Z29wrlA2
|
||||
wrIYaAkmVaHny7wsOmgX01aOnuf2MlUnksK43sjZHdIo/m+sDKwwY1cCgYEAzHx4
|
||||
mwUDMdRF4qpDKJhthraBNejRextNQQYsHVnNaMwZ4aeQcH5l85Cgjm7VpGlbVyBQ
|
||||
h4zwFvllImp3D2U3mjVkV8Tm9ID98eWvw2YDzBnS3P3SysajD23Z+BXSG9GNv/8k
|
||||
oAm+bVlvnJy4haK2AcIMk1YFuDuAOmy73abk7iUCgYEAj4qVM1sq/eKfAM1LJRfg
|
||||
/jbIX+hYfMePD8pUUWygIra6jJ4tjtvSBZrwyPb3IImjY3W/KoP0AcVjxAeORohz
|
||||
dkP1a6L8LiuFxSuzpdW5BkyuebxGhXCOWKVVvMDC4jLTPVCUXlHSv3GFemCjjgXM
|
||||
QlNxT5rjsha4Gr8nLIsJAacCgYA4VA1Q/pd7sXKy1p37X8nD8yAyvnh+Be5I/C9I
|
||||
woUP2jFC9MqYAmmJJ4ziz2swiAkuPeuQ+2Tjnz2ZtmQnrIUdiJmkh8vrDGFnshKx
|
||||
q7deELsCPzVCwGcIiAUkDra7DQWUHu9y2lxHePyC0rUNst2aLF8UcvzOXC2danhx
|
||||
vViQtQKBgCmZ7YavE/GNWww8N3xHBJ6UPmUuhQlnAbgNCcdyz30MevBg/JbyUTs2
|
||||
slftTH15QusJ1UoITnnZuFJ40LqDvh8UhiK09ffM/IbUx839/m2vUOdFZB/WNn9g
|
||||
Cy0LzddU4KE8JZ/tlk68+hM5fjLLA0aqSunaql5CKfplwLu8x1hL
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
||||
keys = map[string]string{
|
||||
"ssh_host_dsa_key": `-----BEGIN DSA PRIVATE KEY-----
|
||||
MIIBugIBAAKBgQDe2SIKvZdBp+InawtSXH0NotiMPhm3udyu4hh/E+icMz264kDX
|
||||
v+sV7ddnSQGQWZ/eVU7Jtx29dCMD1VlFpEd7yGKzmdwJIeA+YquNWoqBRQEJsWWS
|
||||
7Fsfvv83dA/DTNIQfOY3+TIs6Mb9vagbgQMU3JUWEhbLE9LCEU6UwwRlpQIVAL4p
|
||||
JF83SwpE8Jx6KnDpR89npkl/AoGAAy00TdDnAXvStwrZiAFbjZi8xDmPa9WwpfhJ
|
||||
Rkno45TthDLrS+WmqY8/LTwlqZdOBtoBAynMJfKkUiZM21lWWpL1hRKYdwBlIBy5
|
||||
XdR2/6wcPSuZ0tCQhDBTstX0Q3P1j198KGKvzy7q9vILKQwtSRqLS1y4JJERafdO
|
||||
E+9CnGwCgYBz0WwBe2EZtGhGhBdnelTIBeo7PIsr0PzqxQj+dc8PBl8K9FfhRyOp
|
||||
U39stUvoUxE9vaIFrY1P5xENjLFnPf+hlcuf40GUWEssW9YWPOaBp8afa9hY5Sxs
|
||||
pvNR6eZFEFOJnx/ZgcA4g+vbrgGi5cM0W470mbGw2CkfJQUafdoIgAIUF+2I9kZe
|
||||
2FTBuC9uacqczDlc+0k=
|
||||
-----END DSA PRIVATE KEY-----`,
|
||||
"ssh_host_rsa_key": `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAuf76Ue2Wtae9oDtaS6rIJgO7iCFTsZUTW9LBsvx/2nli6jKU
|
||||
d9tUbBRzgdbnRLJ32UljXhERuB/axlrX8/lBzUZ+oYiM0KkEEOXY1z/bcMxdRxGF
|
||||
XHuf4uXvyC2XyA4+ZvBeS4j1QFyIHZ62o7gAlKMTjiek3B4AQEJAlCLmhH3jB8wc
|
||||
K/IYXAOlNGM5G44/ZLQpTi8diOV6DLs7tJ7rtEQedOEJfZng5rwp0USFkqcbfDbe
|
||||
9/hk0J32jZvOtZNBokYtBb4YEdIiWBzzNtHzU3Dzw61+TKVXaH5HaIvzL9iMrw9f
|
||||
kJbJyogfZk9BJfemEN+xqP72jlhE8LXNhpTxFQIDAQABAoIBAHbdf+Y5+5XuNF6h
|
||||
b8xpwW2h9whBnDYiOnP1VfroKWFbMB7R4lZS4joMO+FfkP8zOyqvHwTvza4pFWys
|
||||
g9SUmDvy8FyVYsC7MzEFYzX0xm3o/Te898ip7P1Zy4rXsGeWysSImwqU5X+TYx3i
|
||||
33/zyNM1APtZVJ+jwK9QZ+sD/uPuZK2yS03HGSMZq6ebdoOSaYhluKrxXllSLO1J
|
||||
KJxDiDdy2lEFw0W8HcI3ly1lg6OI+TRqqaCcLVNF4fNJmYIFM+2VEI9BdgynIh0Q
|
||||
pMZlJKgaEBcSqCymnTK81ohYD1cV4st2B0km3Sw35Rl04Ij5ITeiya3hp8VfE6UY
|
||||
PljkA6UCgYEA4811FTFj+kzNZ86C4OW1T5sM4NZt8gcz6CSvVnl+bDzbEOMMyzP7
|
||||
2I9zKsR5ApdodH2m8d+RUw1Oe0bNGW5xig/DH/hn9lLQaO52JAi0we8A94dUUMSq
|
||||
fUk9jKZEXpP/MlfTdJaPos9mxT7z8jREQxIiqH9AV0rLVDOCfDbSWj8CgYEA0QTE
|
||||
IAUuki3UUqYKzLQrh/QmhY5KTx5amNW9XZ2VGtJvDPJrtBSBZlPEuXZAc4eBWEc7
|
||||
U3Y9QwsalzupU6Yi6+gmofaXs8xJnj+jKth1DnJvrbLLGlSmf2Ijnwt22TyFUOtt
|
||||
UAknpjHutDjQPf7pUGWaCPgwwKFsdB8EBjpJF6sCgYAfXesBQAvEK08dPBJJZVfR
|
||||
3kenrd71tIgxLtv1zETcIoUHjjv0vvOunhH9kZAYC0EWyTZzl5UrGmn0D4uuNMbt
|
||||
e74iaNHn2P9Zc3xQ+eHp0j8P1lKFzI6tMaiH9Vz0qOw6wl0bcJ/WizhbcI+migvc
|
||||
MGMVUHBLlMDqly0gbWwJgQKBgQCgtb9ut01FjANSwORQ3L8Tu3/a9Lrh9n7GQKFn
|
||||
V4CLrP1BwStavOF5ojMCPo/zxF6JV8ufsqwL3n/FhFP/QyBarpb1tTqTPiHkkR2O
|
||||
Ffx67TY9IdnUFv4lt3mYEiKBiW0f+MSF42Qe/wmAfKZw5IzUCirTdrFVi0huSGK5
|
||||
vxrwHQKBgHZ7RoC3I2f6F5fflA2ZAe9oJYC7XT624rY7VeOBwK0W0F47iV3euPi/
|
||||
pKvLIBLcWL1Lboo+girnmSZtIYg2iLS3b4T9VFcKWg0y4AVwmhMWe9jWIltfWAAX
|
||||
9l0lNikMRGAx3eXudKXEtbGt3/cUzPVaQUHy5LiBxkxnFxgaJPXs
|
||||
-----END RSA PRIVATE KEY-----`,
|
||||
"ssh_host_ecdsa_key": `-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEINGWx0zo6fhJ/0EAfrPzVFyFC9s18lBt3cRoEDhS3ARooAoGCCqGSM49
|
||||
AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+
|
||||
6/ZOXRnPmNAlLUqjShUsUBBngG0u2fqEqA==
|
||||
-----END EC PRIVATE KEY-----`,
|
||||
"authorized_keys": `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEX/dPu4PmtvgK3La9zioCEDrJyUr6xEIK7Pr+rLgydcqWTU/kt7w7gKjOw4vvzgHfjKl09CWyvgb+y5dCiTk9MxI+erGNhs3pwaoS+EavAbawB7iEqYyTep3YaJK+4RJ4OX7ZlXMAIMrTL+UVrK89t56hCkFYaAgo3VY+z6rb/b3bDBYtE1Y2tS7C3au73aDgeb9psIrSV86ucKBTl5X62FnYiyGd++xCnLB6uLximM5OKXfLzJQNS/QyZyk12g3D8y69Xw1GzCSKX1u1+MQboyf0HJcG2ryUCLHdcDVppApyHx2OLq53hlkQ/yxdflDqCqAE4j+doagSsIfC1T2T user@host`,
|
||||
}
|
||||
)
|
||||
|
||||
func TestMarshalParsePublicKey(t *testing.T) {
|
||||
pub := getTestPublicKey(t)
|
||||
|
||||
authKeys := ssh.MarshalAuthorizedKey(pub)
|
||||
actualFields := strings.Fields(string(authKeys))
|
||||
if len(actualFields) == 0 {
|
||||
t.Fatalf("failed authKeys: %v", authKeys)
|
||||
}
|
||||
|
||||
// drop the comment
|
||||
expectedFields := strings.Fields(keys["authorized_keys"])[0:2]
|
||||
|
||||
if !reflect.DeepEqual(actualFields, expectedFields) {
|
||||
t.Errorf("got %v, expected %v", actualFields, expectedFields)
|
||||
}
|
||||
|
||||
actPub, _, _, _, ok := ssh.ParseAuthorizedKey([]byte(keys["authorized_keys"]))
|
||||
if !ok {
|
||||
t.Fatalf("cannot parse %v", keys["authorized_keys"])
|
||||
}
|
||||
if !reflect.DeepEqual(actPub, pub) {
|
||||
t.Errorf("got %v, expected %v", actPub, pub)
|
||||
}
|
||||
}
|
||||
|
||||
type authResult struct {
|
||||
pubKey interface{} //*rsa.PublicKey
|
||||
options []string
|
||||
comments string
|
||||
rest string
|
||||
ok bool
|
||||
}
|
||||
|
||||
func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) {
|
||||
rest := authKeys
|
||||
var values []authResult
|
||||
for len(rest) > 0 {
|
||||
var r authResult
|
||||
r.pubKey, r.comments, r.options, rest, r.ok = ssh.ParseAuthorizedKey(rest)
|
||||
r.rest = string(rest)
|
||||
values = append(values, r)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(values, expected) {
|
||||
t.Errorf("got %q, expected %q", values, expected)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getTestPublicKey(t *testing.T) ssh.PublicKey {
|
||||
priv, err := ssh.ParsePrivateKey([]byte(testClientPrivateKey))
|
||||
if err != nil {
|
||||
t.Fatalf("ParsePrivateKey: %v", err)
|
||||
}
|
||||
|
||||
return priv.PublicKey()
|
||||
}
|
||||
|
||||
func TestAuth(t *testing.T) {
|
||||
pub := getTestPublicKey(t)
|
||||
rest2 := strings.Join(authWithOptions[3:], "\n")
|
||||
rest3 := strings.Join(authWithOptions[6:], "\n")
|
||||
testAuthorizedKeys(t, []byte(authOptions), []authResult{
|
||||
{pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true},
|
||||
{pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true},
|
||||
{nil, nil, "", "", false},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuthWithCRLF(t *testing.T) {
|
||||
pub := getTestPublicKey(t)
|
||||
rest2 := strings.Join(authWithOptions[3:], "\r\n")
|
||||
rest3 := strings.Join(authWithOptions[6:], "\r\n")
|
||||
testAuthorizedKeys(t, []byte(authWithCRLF), []authResult{
|
||||
{pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true},
|
||||
{pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true},
|
||||
{nil, nil, "", "", false},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuthWithQuotedSpaceInEnv(t *testing.T) {
|
||||
pub := getTestPublicKey(t)
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{
|
||||
{pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuthWithQuotedCommaInEnv(t *testing.T) {
|
||||
pub := getTestPublicKey(t)
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{
|
||||
{pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuthWithQuotedQuoteInEnv(t *testing.T) {
|
||||
pub := getTestPublicKey(t)
|
||||
testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{
|
||||
{pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true},
|
||||
})
|
||||
|
||||
testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{
|
||||
{pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAuthWithInvalidSpace(t *testing.T) {
|
||||
testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{
|
||||
{nil, nil, "", "", false},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuthWithMissingQuote(t *testing.T) {
|
||||
pub := getTestPublicKey(t)
|
||||
testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{
|
||||
{pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInvalidEntry(t *testing.T) {
|
||||
_, _, _, _, ok := ssh.ParseAuthorizedKey(authInvalid)
|
||||
if ok {
|
||||
t.Errorf("Expected invalid entry, returned valid entry")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user