mirror of
https://github.com/golang/go
synced 2024-11-21 21:04:41 -07:00
exp/ssh: allow for msgUserAuthBanner during authentication
The SSH spec allows for the server to send a banner message to the client at any point during the authentication process. Currently the ssh client auth types all assume that the first response from the server after issuing a userAuthRequestMsg will be one of a couple of possible authentication success/failure messages. This means that client authentication breaks if the ssh server being connected to has a banner message configured. This changeset refactors the noneAuth, passwordAuth and publickeyAuth types' auth() function and allows for msgUserAuthBanner during authentication. R=golang-dev, rsc, dave, agl CC=golang-dev https://golang.org/cl/5432065
This commit is contained in:
parent
6f0ef845e6
commit
bd9dc3d55f
@ -79,19 +79,7 @@ func (n *noneAuth) auth(session []byte, user string, t *transport, rand io.Reade
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
packet, err := t.readPacket()
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
switch packet[0] {
|
||||
case msgUserAuthSuccess:
|
||||
return true, nil, nil
|
||||
case msgUserAuthFailure:
|
||||
msg := decode(packet).(*userAuthFailureMsg)
|
||||
return false, msg.Methods, nil
|
||||
}
|
||||
return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
|
||||
return handleAuthResponse(t)
|
||||
}
|
||||
|
||||
func (n *noneAuth) method() string {
|
||||
@ -127,19 +115,7 @@ func (p *passwordAuth) auth(session []byte, user string, t *transport, rand io.R
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
packet, err := t.readPacket()
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
switch packet[0] {
|
||||
case msgUserAuthSuccess:
|
||||
return true, nil, nil
|
||||
case msgUserAuthFailure:
|
||||
msg := decode(packet).(*userAuthFailureMsg)
|
||||
return false, msg.Methods, nil
|
||||
}
|
||||
return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
|
||||
return handleAuthResponse(t)
|
||||
}
|
||||
|
||||
func (p *passwordAuth) method() string {
|
||||
@ -173,7 +149,6 @@ type publickeyAuth struct {
|
||||
ClientKeyring
|
||||
}
|
||||
|
||||
func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) {
|
||||
type publickeyAuthMsg struct {
|
||||
User string
|
||||
Service string
|
||||
@ -183,10 +158,12 @@ func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.
|
||||
HasSig bool
|
||||
Algoname string
|
||||
Pubkey string
|
||||
// Sig is defined as []byte so marshal will exclude it during the query phase
|
||||
// Sig is defined as []byte so marshal will exclude it during validateKey
|
||||
Sig []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) {
|
||||
|
||||
// Authentication is performed in two stages. The first stage sends an
|
||||
// enquiry to test if each key is acceptable to the remote. The second
|
||||
// stage attempts to authenticate with the valid keys obtained in the
|
||||
@ -204,33 +181,13 @@ func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.
|
||||
// no more keys in the keyring
|
||||
break
|
||||
}
|
||||
pubkey := serializePublickey(key)
|
||||
algoname := algoName(key)
|
||||
msg := publickeyAuthMsg{
|
||||
User: user,
|
||||
Service: serviceSSH,
|
||||
Method: p.method(),
|
||||
HasSig: false,
|
||||
Algoname: algoname,
|
||||
Pubkey: string(pubkey),
|
||||
}
|
||||
if err := t.writePacket(marshal(msgUserAuthRequest, msg)); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
packet, err := t.readPacket()
|
||||
|
||||
if ok, err := p.validateKey(key, user, t); ok {
|
||||
validKeys[index] = key
|
||||
} else {
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
switch packet[0] {
|
||||
case msgUserAuthPubKeyOk:
|
||||
msg := decode(packet).(*userAuthPubKeyOkMsg)
|
||||
if msg.Algo != algoname || msg.PubKey != string(pubkey) {
|
||||
continue
|
||||
}
|
||||
validKeys[index] = key
|
||||
case msgUserAuthFailure:
|
||||
default:
|
||||
return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
|
||||
}
|
||||
index++
|
||||
}
|
||||
@ -265,26 +222,63 @@ func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.
|
||||
if err := t.writePacket(p); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
packet, err := t.readPacket()
|
||||
success, methods, err := handleAuthResponse(t)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
switch packet[0] {
|
||||
case msgUserAuthSuccess:
|
||||
return true, nil, nil
|
||||
case msgUserAuthFailure:
|
||||
msg := decode(packet).(*userAuthFailureMsg)
|
||||
methods = msg.Methods
|
||||
continue
|
||||
case msgDisconnect:
|
||||
return false, nil, io.EOF
|
||||
default:
|
||||
return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
|
||||
if success {
|
||||
return success, methods, err
|
||||
}
|
||||
}
|
||||
return false, methods, nil
|
||||
}
|
||||
|
||||
// validateKey validates the key provided it is acceptable to the server.
|
||||
func (p *publickeyAuth) validateKey(key interface{}, user string, t *transport) (bool, error) {
|
||||
pubkey := serializePublickey(key)
|
||||
algoname := algoName(key)
|
||||
msg := publickeyAuthMsg{
|
||||
User: user,
|
||||
Service: serviceSSH,
|
||||
Method: p.method(),
|
||||
HasSig: false,
|
||||
Algoname: algoname,
|
||||
Pubkey: string(pubkey),
|
||||
}
|
||||
if err := t.writePacket(marshal(msgUserAuthRequest, msg)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return p.confirmKeyAck(key, t)
|
||||
}
|
||||
|
||||
func (p *publickeyAuth) confirmKeyAck(key interface{}, t *transport) (bool, error) {
|
||||
pubkey := serializePublickey(key)
|
||||
algoname := algoName(key)
|
||||
|
||||
for {
|
||||
packet, err := t.readPacket()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
switch packet[0] {
|
||||
case msgUserAuthBanner:
|
||||
// TODO(gpaul): add callback to present the banner to the user
|
||||
case msgUserAuthPubKeyOk:
|
||||
msg := decode(packet).(*userAuthPubKeyOkMsg)
|
||||
if msg.Algo != algoname || msg.PubKey != string(pubkey) {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
case msgUserAuthFailure:
|
||||
return false, nil
|
||||
default:
|
||||
return false, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func (p *publickeyAuth) method() string {
|
||||
return "publickey"
|
||||
}
|
||||
@ -293,3 +287,30 @@ func (p *publickeyAuth) method() string {
|
||||
func ClientAuthPublickey(impl ClientKeyring) ClientAuth {
|
||||
return &publickeyAuth{impl}
|
||||
}
|
||||
|
||||
// handleAuthResponse returns whether the preceding authentication request succeeded
|
||||
// along with a list of remaining authentication methods to try next and
|
||||
// an error if an unexpected response was received.
|
||||
func handleAuthResponse(t *transport) (bool, []string, error) {
|
||||
for {
|
||||
packet, err := t.readPacket()
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
switch packet[0] {
|
||||
case msgUserAuthBanner:
|
||||
// TODO: add callback to present the banner to the user
|
||||
case msgUserAuthFailure:
|
||||
msg := decode(packet).(*userAuthFailureMsg)
|
||||
return false, msg.Methods, nil
|
||||
case msgUserAuthSuccess:
|
||||
return true, nil, nil
|
||||
case msgDisconnect:
|
||||
return false, nil, io.EOF
|
||||
default:
|
||||
return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user