diff options
Diffstat (limited to 'libgo/go/exp/ssh/client_auth.go')
-rw-r--r-- | libgo/go/exp/ssh/client_auth.go | 148 |
1 files changed, 143 insertions, 5 deletions
diff --git a/libgo/go/exp/ssh/client_auth.go b/libgo/go/exp/ssh/client_auth.go index 0089d0c..25f9e21 100644 --- a/libgo/go/exp/ssh/client_auth.go +++ b/libgo/go/exp/ssh/client_auth.go @@ -6,10 +6,11 @@ package ssh import ( "errors" + "io" ) // authenticate authenticates with the remote server. See RFC 4252. -func (c *ClientConn) authenticate() error { +func (c *ClientConn) authenticate(session []byte) error { // initiate user auth session if err := c.writePacket(marshal(msgServiceRequest, serviceRequestMsg{serviceUserAuth})); err != nil { return err @@ -26,7 +27,7 @@ func (c *ClientConn) authenticate() error { // then any untried methods suggested by the server. tried, remain := make(map[string]bool), make(map[string]bool) for auth := ClientAuth(new(noneAuth)); auth != nil; { - ok, methods, err := auth.auth(c.config.User, c.transport) + ok, methods, err := auth.auth(session, c.config.User, c.transport, c.config.rand()) if err != nil { return err } @@ -60,7 +61,7 @@ type ClientAuth interface { // Returns true if authentication is successful. // If authentication is not successful, a []string of alternative // method names is returned. - auth(user string, t *transport) (bool, []string, error) + auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) // method returns the RFC 4252 method name. method() string @@ -69,7 +70,7 @@ type ClientAuth interface { // "none" authentication, RFC 4252 section 5.2. type noneAuth int -func (n *noneAuth) auth(user string, t *transport) (bool, []string, error) { +func (n *noneAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { if err := t.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{ User: user, Service: serviceSSH, @@ -102,7 +103,7 @@ type passwordAuth struct { ClientPassword } -func (p *passwordAuth) auth(user string, t *transport) (bool, []string, error) { +func (p *passwordAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { type passwordAuthMsg struct { User string Service string @@ -155,3 +156,140 @@ type ClientPassword interface { func ClientAuthPassword(impl ClientPassword) ClientAuth { return &passwordAuth{impl} } + +// ClientKeyring implements access to a client key ring. +type ClientKeyring interface { + // Key returns the i'th rsa.Publickey or dsa.Publickey, or nil if + // no key exists at i. + Key(i int) (key interface{}, err error) + + // Sign returns a signature of the given data using the i'th key + // and the supplied random source. + Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) +} + +// "publickey" authentication, RFC 4252 Section 7. +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 + Method string + // HasSig indicates to the reciver packet that the auth request is signed and + // should be used for authentication of the request. + HasSig bool + Algoname string + Pubkey string + // Sig is defined as []byte so marshal will exclude it during the query phase + Sig []byte `ssh:"rest"` + } + + // Authentication is performed in two stages. The first stage sends an + // enquiry to test if each key is acceptable to the remote. The second + // stage attempts to authenticate with the valid keys obtained in the + // first stage. + + var index int + // a map of public keys to their index in the keyring + validKeys := make(map[int]interface{}) + for { + key, err := p.Key(index) + if err != nil { + return false, nil, err + } + if key == nil { + // 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 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++ + } + + // methods that may continue if this auth is not successful. + var methods []string + for i, key := range validKeys { + pubkey := serializePublickey(key) + algoname := algoName(key) + sign, err := p.Sign(i, rand, buildDataSignedForAuth(session, userAuthRequestMsg{ + User: user, + Service: serviceSSH, + Method: p.method(), + }, []byte(algoname), pubkey)) + if err != nil { + return false, nil, err + } + // manually wrap the serialized signature in a string + s := serializeSignature(algoname, sign) + sig := make([]byte, stringLength(s)) + marshalString(sig, s) + msg := publickeyAuthMsg{ + User: user, + Service: serviceSSH, + Method: p.method(), + HasSig: true, + Algoname: algoname, + Pubkey: string(pubkey), + Sig: sig, + } + p := marshal(msgUserAuthRequest, msg) + if err := t.writePacket(p); err != nil { + 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) + methods = msg.Methods + continue + case msgDisconnect: + return false, nil, io.EOF + default: + return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]} + } + } + return false, methods, nil +} + +func (p *publickeyAuth) method() string { + return "publickey" +} + +// ClientAuthPublickey returns a ClientAuth using public key authentication. +func ClientAuthPublickey(impl ClientKeyring) ClientAuth { + return &publickeyAuth{impl} +} |