github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/third_party/code.google.com/p/go.crypto/ssh/client_auth.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ssh 6 7 import ( 8 "errors" 9 "io" 10 ) 11 12 // authenticate authenticates with the remote server. See RFC 4252. 13 func (c *ClientConn) authenticate(session []byte) error { 14 // initiate user auth session 15 if err := c.writePacket(marshal(msgServiceRequest, serviceRequestMsg{serviceUserAuth})); err != nil { 16 return err 17 } 18 packet, err := c.readPacket() 19 if err != nil { 20 return err 21 } 22 var serviceAccept serviceAcceptMsg 23 if err := unmarshal(&serviceAccept, packet, msgServiceAccept); err != nil { 24 return err 25 } 26 // during the authentication phase the client first attempts the "none" method 27 // then any untried methods suggested by the server. 28 tried, remain := make(map[string]bool), make(map[string]bool) 29 for auth := ClientAuth(new(noneAuth)); auth != nil; { 30 ok, methods, err := auth.auth(session, c.config.User, c.transport, c.config.rand()) 31 if err != nil { 32 return err 33 } 34 if ok { 35 // success 36 return nil 37 } 38 tried[auth.method()] = true 39 delete(remain, auth.method()) 40 for _, meth := range methods { 41 if tried[meth] { 42 // if we've tried meth already, skip it. 43 continue 44 } 45 remain[meth] = true 46 } 47 auth = nil 48 for _, a := range c.config.Auth { 49 if remain[a.method()] { 50 auth = a 51 break 52 } 53 } 54 } 55 return errors.New("ssh: unable to authenticate, no supported methods remain") 56 } 57 58 // A ClientAuth represents an instance of an RFC 4252 authentication method. 59 type ClientAuth interface { 60 // auth authenticates user over transport t. 61 // Returns true if authentication is successful. 62 // If authentication is not successful, a []string of alternative 63 // method names is returned. 64 auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) 65 66 // method returns the RFC 4252 method name. 67 method() string 68 } 69 70 // "none" authentication, RFC 4252 section 5.2. 71 type noneAuth int 72 73 func (n *noneAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { 74 if err := t.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{ 75 User: user, 76 Service: serviceSSH, 77 Method: "none", 78 })); err != nil { 79 return false, nil, err 80 } 81 82 return handleAuthResponse(t) 83 } 84 85 func (n *noneAuth) method() string { 86 return "none" 87 } 88 89 // "password" authentication, RFC 4252 Section 8. 90 type passwordAuth struct { 91 ClientPassword 92 } 93 94 func (p *passwordAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { 95 type passwordAuthMsg struct { 96 User string 97 Service string 98 Method string 99 Reply bool 100 Password string 101 } 102 103 pw, err := p.Password(user) 104 if err != nil { 105 return false, nil, err 106 } 107 108 if err := t.writePacket(marshal(msgUserAuthRequest, passwordAuthMsg{ 109 User: user, 110 Service: serviceSSH, 111 Method: "password", 112 Reply: false, 113 Password: pw, 114 })); err != nil { 115 return false, nil, err 116 } 117 118 return handleAuthResponse(t) 119 } 120 121 func (p *passwordAuth) method() string { 122 return "password" 123 } 124 125 // A ClientPassword implements access to a client's passwords. 126 type ClientPassword interface { 127 // Password returns the password to use for user. 128 Password(user string) (password string, err error) 129 } 130 131 // ClientAuthPassword returns a ClientAuth using password authentication. 132 func ClientAuthPassword(impl ClientPassword) ClientAuth { 133 return &passwordAuth{impl} 134 } 135 136 // ClientKeyring implements access to a client key ring. 137 type ClientKeyring interface { 138 // Key returns the i'th rsa.Publickey or dsa.Publickey, or nil if 139 // no key exists at i. 140 Key(i int) (key interface{}, err error) 141 142 // Sign returns a signature of the given data using the i'th key 143 // and the supplied random source. 144 Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) 145 } 146 147 // "publickey" authentication, RFC 4252 Section 7. 148 type publickeyAuth struct { 149 ClientKeyring 150 } 151 152 type publickeyAuthMsg struct { 153 User string 154 Service string 155 Method string 156 // HasSig indicates to the reciver packet that the auth request is signed and 157 // should be used for authentication of the request. 158 HasSig bool 159 Algoname string 160 Pubkey string 161 // Sig is defined as []byte so marshal will exclude it during validateKey 162 Sig []byte `ssh:"rest"` 163 } 164 165 func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { 166 167 // Authentication is performed in two stages. The first stage sends an 168 // enquiry to test if each key is acceptable to the remote. The second 169 // stage attempts to authenticate with the valid keys obtained in the 170 // first stage. 171 172 var index int 173 // a map of public keys to their index in the keyring 174 validKeys := make(map[int]interface{}) 175 for { 176 key, err := p.Key(index) 177 if err != nil { 178 return false, nil, err 179 } 180 if key == nil { 181 // no more keys in the keyring 182 break 183 } 184 185 if ok, err := p.validateKey(key, user, t); ok { 186 validKeys[index] = key 187 } else { 188 if err != nil { 189 return false, nil, err 190 } 191 } 192 index++ 193 } 194 195 // methods that may continue if this auth is not successful. 196 var methods []string 197 for i, key := range validKeys { 198 pubkey := serializePublickey(key) 199 algoname := algoName(key) 200 sign, err := p.Sign(i, rand, buildDataSignedForAuth(session, userAuthRequestMsg{ 201 User: user, 202 Service: serviceSSH, 203 Method: p.method(), 204 }, []byte(algoname), pubkey)) 205 if err != nil { 206 return false, nil, err 207 } 208 // manually wrap the serialized signature in a string 209 s := serializeSignature(algoname, sign) 210 sig := make([]byte, stringLength(s)) 211 marshalString(sig, s) 212 msg := publickeyAuthMsg{ 213 User: user, 214 Service: serviceSSH, 215 Method: p.method(), 216 HasSig: true, 217 Algoname: algoname, 218 Pubkey: string(pubkey), 219 Sig: sig, 220 } 221 p := marshal(msgUserAuthRequest, msg) 222 if err := t.writePacket(p); err != nil { 223 return false, nil, err 224 } 225 success, methods, err := handleAuthResponse(t) 226 if err != nil { 227 return false, nil, err 228 } 229 if success { 230 return success, methods, err 231 } 232 } 233 return false, methods, nil 234 } 235 236 // validateKey validates the key provided it is acceptable to the server. 237 func (p *publickeyAuth) validateKey(key interface{}, user string, t *transport) (bool, error) { 238 pubkey := serializePublickey(key) 239 algoname := algoName(key) 240 msg := publickeyAuthMsg{ 241 User: user, 242 Service: serviceSSH, 243 Method: p.method(), 244 HasSig: false, 245 Algoname: algoname, 246 Pubkey: string(pubkey), 247 } 248 if err := t.writePacket(marshal(msgUserAuthRequest, msg)); err != nil { 249 return false, err 250 } 251 252 return p.confirmKeyAck(key, t) 253 } 254 255 func (p *publickeyAuth) confirmKeyAck(key interface{}, t *transport) (bool, error) { 256 pubkey := serializePublickey(key) 257 algoname := algoName(key) 258 259 for { 260 packet, err := t.readPacket() 261 if err != nil { 262 return false, err 263 } 264 switch packet[0] { 265 case msgUserAuthBanner: 266 // TODO(gpaul): add callback to present the banner to the user 267 case msgUserAuthPubKeyOk: 268 msg := decode(packet).(*userAuthPubKeyOkMsg) 269 if msg.Algo != algoname || msg.PubKey != string(pubkey) { 270 return false, nil 271 } 272 return true, nil 273 case msgUserAuthFailure: 274 return false, nil 275 default: 276 return false, UnexpectedMessageError{msgUserAuthSuccess, packet[0]} 277 } 278 } 279 panic("unreachable") 280 } 281 282 func (p *publickeyAuth) method() string { 283 return "publickey" 284 } 285 286 // ClientAuthKeyring returns a ClientAuth using public key authentication. 287 func ClientAuthKeyring(impl ClientKeyring) ClientAuth { 288 return &publickeyAuth{impl} 289 } 290 291 // handleAuthResponse returns whether the preceding authentication request succeeded 292 // along with a list of remaining authentication methods to try next and 293 // an error if an unexpected response was received. 294 func handleAuthResponse(t *transport) (bool, []string, error) { 295 for { 296 packet, err := t.readPacket() 297 if err != nil { 298 return false, nil, err 299 } 300 301 switch packet[0] { 302 case msgUserAuthBanner: 303 // TODO: add callback to present the banner to the user 304 case msgUserAuthFailure: 305 msg := decode(packet).(*userAuthFailureMsg) 306 return false, msg.Methods, nil 307 case msgUserAuthSuccess: 308 return true, nil, nil 309 case msgDisconnect: 310 return false, nil, io.EOF 311 default: 312 return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]} 313 } 314 } 315 panic("unreachable") 316 }