LEFT | RIGHT |
| 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 "fmt" |
| 10 "io" |
| 11 "net" |
| 12 ) |
| 13 |
| 14 // authenticate authenticates with the remote server. See RFC 4252. |
| 15 func (c *ClientConn) authenticate() error { |
| 16 // initiate user auth session |
| 17 if err := c.transport.writePacket(marshal(msgServiceRequest, serviceRequ
estMsg{serviceUserAuth})); err != nil { |
| 18 return err |
| 19 } |
| 20 packet, err := c.transport.readPacket() |
| 21 if err != nil { |
| 22 return err |
| 23 } |
| 24 var serviceAccept serviceAcceptMsg |
| 25 if err := unmarshal(&serviceAccept, packet, msgServiceAccept); err != ni
l { |
| 26 return err |
| 27 } |
| 28 |
| 29 // during the authentication phase the client first attempts the "none"
method |
1 // then any untried methods suggested by the server. | 30 // then any untried methods suggested by the server. |
2 tried, remain := make(map[string]bool), make(map[string]bool) | 31 tried, remain := make(map[string]bool), make(map[string]bool) |
3 for auth := ClientAuth(new(noneAuth)); auth != nil; { | 32 for auth := ClientAuth(new(noneAuth)); auth != nil; { |
4 » » ok, methods, err := auth.auth(session, c.config.User, c.transpor
t, c.config.rand()) | 33 » » ok, methods, err := auth.auth(c.transport.getSessionID(), c.conf
ig.User, c.transport, c.config.rand()) |
5 if err != nil { | 34 if err != nil { |
6 return err | 35 return err |
7 } | 36 } |
| 37 if ok { |
| 38 // success |
| 39 return nil |
| 40 } |
| 41 tried[auth.method()] = true |
| 42 delete(remain, auth.method()) |
| 43 for _, meth := range methods { |
| 44 if tried[meth] { |
| 45 // if we've tried meth already, skip it. |
| 46 continue |
| 47 } |
| 48 remain[meth] = true |
| 49 } |
| 50 auth = nil |
| 51 for _, a := range c.config.Auth { |
| 52 if remain[a.method()] { |
| 53 auth = a |
| 54 break |
| 55 } |
| 56 } |
| 57 } |
| 58 return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no
supported methods remain", keys(tried)) |
| 59 } |
| 60 |
| 61 func keys(m map[string]bool) (s []string) { |
| 62 for k := range m { |
| 63 s = append(s, k) |
| 64 } |
| 65 return |
| 66 } |
| 67 |
| 68 // HostKeyChecker represents a database of known server host keys. |
| 69 type HostKeyChecker interface { |
| 70 // Check is called during the handshake to check server's |
| 71 // public key for unexpected changes. The hostKey argument is |
| 72 // in SSH wire format. It can be parsed using |
| 73 // ssh.ParsePublicKey. The address before DNS resolution is |
| 74 // passed in the addr argument, so the key can also be checked |
| 75 // against the hostname. |
| 76 Check(addr string, remote net.Addr, algorithm string, hostKey []byte) er
ror |
| 77 } |
| 78 |
| 79 // A ClientAuth represents an instance of an RFC 4252 authentication method. |
| 80 type ClientAuth interface { |
| 81 // auth authenticates user over transport t. |
| 82 // Returns true if authentication is successful. |
| 83 // If authentication is not successful, a []string of alternative |
| 84 // method names is returned. |
| 85 auth(session []byte, user string, p packetConn, rand io.Reader) (bool, [
]string, error) |
| 86 |
| 87 // method returns the RFC 4252 method name. |
| 88 method() string |
| 89 } |
| 90 |
| 91 // "none" authentication, RFC 4252 section 5.2. |
| 92 type noneAuth int |
| 93 |
| 94 func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reade
r) (bool, []string, error) { |
| 95 if err := c.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{ |
| 96 User: user, |
| 97 Service: serviceSSH, |
| 98 Method: "none", |
| 99 })); err != nil { |
| 100 return false, nil, err |
| 101 } |
| 102 |
| 103 return handleAuthResponse(c) |
| 104 } |
| 105 |
| 106 func (n *noneAuth) method() string { |
| 107 return "none" |
| 108 } |
| 109 |
| 110 // "password" authentication, RFC 4252 Section 8. |
| 111 type passwordAuth struct { |
| 112 ClientPassword |
| 113 } |
| 114 |
| 115 func (p *passwordAuth) auth(session []byte, user string, c packetConn, rand io.R
eader) (bool, []string, error) { |
| 116 type passwordAuthMsg struct { |
| 117 User string |
| 118 Service string |
| 119 Method string |
| 120 Reply bool |
| 121 Password string |
| 122 } |
| 123 |
| 124 pw, err := p.Password(user) |
| 125 if err != nil { |
| 126 return false, nil, err |
| 127 } |
| 128 |
| 129 if err := c.writePacket(marshal(msgUserAuthRequest, passwordAuthMsg{ |
| 130 User: user, |
| 131 Service: serviceSSH, |
| 132 Method: "password", |
| 133 Reply: false, |
| 134 Password: pw, |
| 135 })); err != nil { |
| 136 return false, nil, err |
| 137 } |
| 138 |
| 139 return handleAuthResponse(c) |
| 140 } |
| 141 |
| 142 func (p *passwordAuth) method() string { |
| 143 return "password" |
| 144 } |
| 145 |
| 146 // A ClientPassword implements access to a client's passwords. |
| 147 type ClientPassword interface { |
| 148 // Password returns the password to use for user. |
| 149 Password(user string) (password string, err error) |
| 150 } |
| 151 |
| 152 // ClientAuthPassword returns a ClientAuth using password authentication. |
| 153 func ClientAuthPassword(impl ClientPassword) ClientAuth { |
| 154 return &passwordAuth{impl} |
| 155 } |
| 156 |
| 157 // ClientKeyring implements access to a client key ring. |
| 158 type ClientKeyring interface { |
| 159 // Key returns the i'th Publickey, or nil if no key exists at i. |
| 160 Key(i int) (key PublicKey, err error) |
| 161 |
| 162 // Sign returns a signature of the given data using the i'th key |
| 163 // and the supplied random source. |
| 164 Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) |
| 165 } |
| 166 |
| 167 // "publickey" authentication, RFC 4252 Section 7. |
| 168 type publickeyAuth struct { |
| 169 ClientKeyring |
| 170 } |
| 171 |
| 172 type publickeyAuthMsg struct { |
| 173 User string |
| 174 Service string |
| 175 Method string |
| 176 // HasSig indicates to the receiver packet that the auth request is sign
ed and |
| 177 // should be used for authentication of the request. |
| 178 HasSig bool |
| 179 Algoname string |
| 180 Pubkey string |
| 181 // Sig is defined as []byte so marshal will exclude it during validateKe
y |
| 182 Sig []byte `ssh:"rest"` |
| 183 } |
| 184 |
| 185 func (p *publickeyAuth) auth(session []byte, user string, c packetConn, rand io.
Reader) (bool, []string, error) { |
| 186 // Authentication is performed in two stages. The first stage sends an |
| 187 // enquiry to test if each key is acceptable to the remote. The second |
| 188 // stage attempts to authenticate with the valid keys obtained in the |
| 189 // first stage. |
| 190 |
| 191 var index int |
| 192 // a map of public keys to their index in the keyring |
| 193 validKeys := make(map[int]PublicKey) |
| 194 for { |
| 195 key, err := p.Key(index) |
| 196 if err != nil { |
| 197 return false, nil, err |
| 198 } |
| 199 if key == nil { |
| 200 // no more keys in the keyring |
| 201 break |
| 202 } |
| 203 |
| 204 if ok, err := p.validateKey(key, user, c); ok { |
| 205 validKeys[index] = key |
| 206 } else { |
| 207 if err != nil { |
| 208 return false, nil, err |
| 209 } |
| 210 } |
| 211 index++ |
| 212 } |
| 213 |
| 214 // methods that may continue if this auth is not successful. |
| 215 var methods []string |
| 216 for i, key := range validKeys { |
| 217 pubkey := MarshalPublicKey(key) |
| 218 algoname := key.PublicKeyAlgo() |
| 219 data := buildDataSignedForAuth(session, userAuthRequestMsg{ |
| 220 User: user, |
| 221 Service: serviceSSH, |
| 222 Method: p.method(), |
| 223 }, []byte(algoname), pubkey) |
| 224 sigBlob, err := p.Sign(i, rand, data) |
| 225 if err != nil { |
| 226 return false, nil, err |
| 227 } |
| 228 // manually wrap the serialized signature in a string |
| 229 s := serializeSignature(key.PublicKeyAlgo(), sigBlob) |
| 230 sig := make([]byte, stringLength(len(s))) |
| 231 marshalString(sig, s) |
| 232 msg := publickeyAuthMsg{ |
| 233 User: user, |
| 234 Service: serviceSSH, |
| 235 Method: p.method(), |
| 236 HasSig: true, |
| 237 Algoname: algoname, |
| 238 Pubkey: string(pubkey), |
| 239 Sig: sig, |
| 240 } |
| 241 p := marshal(msgUserAuthRequest, msg) |
| 242 if err := c.writePacket(p); err != nil { |
| 243 return false, nil, err |
| 244 } |
| 245 success, methods, err := handleAuthResponse(c) |
| 246 if err != nil { |
| 247 return false, nil, err |
| 248 } |
| 249 if success { |
| 250 return success, methods, err |
| 251 } |
| 252 } |
| 253 return false, methods, nil |
| 254 } |
| 255 |
| 256 // validateKey validates the key provided it is acceptable to the server. |
| 257 func (p *publickeyAuth) validateKey(key PublicKey, user string, c packetConn) (b
ool, error) { |
| 258 pubkey := MarshalPublicKey(key) |
| 259 algoname := key.PublicKeyAlgo() |
| 260 msg := publickeyAuthMsg{ |
| 261 User: user, |
| 262 Service: serviceSSH, |
| 263 Method: p.method(), |
| 264 HasSig: false, |
| 265 Algoname: algoname, |
| 266 Pubkey: string(pubkey), |
| 267 } |
| 268 if err := c.writePacket(marshal(msgUserAuthRequest, msg)); err != nil { |
| 269 return false, err |
| 270 } |
| 271 |
| 272 return p.confirmKeyAck(key, c) |
| 273 } |
| 274 |
| 275 func (p *publickeyAuth) confirmKeyAck(key PublicKey, c packetConn) (bool, error)
{ |
| 276 pubkey := MarshalPublicKey(key) |
| 277 algoname := key.PublicKeyAlgo() |
| 278 |
| 279 for { |
| 280 packet, err := c.readPacket() |
| 281 if err != nil { |
| 282 return false, err |
| 283 } |
| 284 switch packet[0] { |
| 285 case msgUserAuthBanner: |
| 286 // TODO(gpaul): add callback to present the banner to th
e user |
| 287 case msgUserAuthPubKeyOk: |
| 288 msg := userAuthPubKeyOkMsg{} |
| 289 if err := unmarshal(&msg, packet, msgUserAuthPubKeyOk);
err != nil { |
| 290 return false, err |
| 291 } |
| 292 if msg.Algo != algoname || msg.PubKey != string(pubkey)
{ |
| 293 return false, nil |
| 294 } |
| 295 return true, nil |
| 296 case msgUserAuthFailure: |
| 297 return false, nil |
| 298 default: |
| 299 return false, UnexpectedMessageError{msgUserAuthSuccess,
packet[0]} |
| 300 } |
| 301 } |
| 302 panic("unreachable") |
| 303 } |
| 304 |
| 305 func (p *publickeyAuth) method() string { |
| 306 return "publickey" |
| 307 } |
| 308 |
| 309 // ClientAuthKeyring returns a ClientAuth using public key authentication. |
| 310 func ClientAuthKeyring(impl ClientKeyring) ClientAuth { |
| 311 return &publickeyAuth{impl} |
| 312 } |
| 313 |
| 314 // handleAuthResponse returns whether the preceding authentication request succe
eded |
| 315 // along with a list of remaining authentication methods to try next and |
| 316 // an error if an unexpected response was received. |
| 317 func handleAuthResponse(c packetConn) (bool, []string, error) { |
| 318 for { |
| 319 packet, err := c.readPacket() |
| 320 if err != nil { |
| 321 return false, nil, err |
| 322 } |
| 323 |
| 324 switch packet[0] { |
| 325 case msgUserAuthBanner: |
| 326 // TODO: add callback to present the banner to the user |
| 327 case msgUserAuthFailure: |
| 328 msg := userAuthFailureMsg{} |
| 329 if err := unmarshal(&msg, packet, msgUserAuthFailure); e
rr != nil { |
| 330 return false, nil, err |
| 331 } |
| 332 return false, msg.Methods, nil |
| 333 case msgUserAuthSuccess: |
| 334 return true, nil, nil |
| 335 case msgDisconnect: |
| 336 return false, nil, io.EOF |
| 337 default: |
| 338 return false, nil, UnexpectedMessageError{msgUserAuthSuc
cess, packet[0]} |
| 339 } |
| 340 } |
| 341 panic("unreachable") |
| 342 } |
| 343 |
| 344 // ClientAuthAgent returns a ClientAuth using public key authentication via |
| 345 // an agent. |
| 346 func ClientAuthAgent(agent *AgentClient) ClientAuth { |
| 347 return ClientAuthKeyring(&agentKeyring{agent: agent}) |
| 348 } |
| 349 |
| 350 // agentKeyring implements ClientKeyring. |
| 351 type agentKeyring struct { |
| 352 agent *AgentClient |
| 353 keys []*AgentKey |
| 354 } |
| 355 |
| 356 func (kr *agentKeyring) Key(i int) (key PublicKey, err error) { |
| 357 if kr.keys == nil { |
| 358 if kr.keys, err = kr.agent.RequestIdentities(); err != nil { |
| 359 return |
| 360 } |
| 361 } |
| 362 if i >= len(kr.keys) { |
| 363 return |
| 364 } |
| 365 return kr.keys[i].Key() |
| 366 } |
| 367 |
| 368 func (kr *agentKeyring) Sign(i int, rand io.Reader, data []byte) (sig []byte, er
r error) { |
| 369 var key PublicKey |
| 370 if key, err = kr.Key(i); err != nil { |
| 371 return |
| 372 } |
| 373 if key == nil { |
| 374 return nil, errors.New("ssh: key index out of range") |
| 375 } |
| 376 if sig, err = kr.agent.SignRequest(key, data); err != nil { |
| 377 return |
| 378 } |
| 379 |
| 380 // Unmarshal the signature. |
| 381 |
| 382 var ok bool |
| 383 if _, sig, ok = parseString(sig); !ok { |
| 384 return nil, errors.New("ssh: malformed signature response from a
gent") |
| 385 } |
| 386 if sig, _, ok = parseString(sig); !ok { |
| 387 return nil, errors.New("ssh: malformed signature response from a
gent") |
| 388 } |
| 389 return sig, nil |
| 390 } |
| 391 |
| 392 // ClientKeyboardInteractive should prompt the user for the given |
| 393 // questions. |
| 394 type ClientKeyboardInteractive interface { |
| 395 // Challenge should print the questions, optionally disabling |
| 396 // echoing (eg. for passwords), and return all the answers. |
| 397 // Challenge may be called multiple times in a single |
| 398 // session. After successful authentication, the server may |
| 399 // send a challenge with no questions, for which the user and |
| 400 // instruction messages should be printed. RFC 4256 section |
| 401 // 3.3 details how the UI should behave for both CLI and |
| 402 // GUI environments. |
| 403 Challenge(user, instruction string, questions []string, echos []bool) ([
]string, error) |
| 404 } |
| 405 |
| 406 // ClientAuthKeyboardInteractive returns a ClientAuth using a |
| 407 // prompt/response sequence controlled by the server. |
| 408 func ClientAuthKeyboardInteractive(impl ClientKeyboardInteractive) ClientAuth { |
| 409 return &keyboardInteractiveAuth{impl} |
| 410 } |
| 411 |
| 412 type keyboardInteractiveAuth struct { |
| 413 ClientKeyboardInteractive |
| 414 } |
| 415 |
| 416 func (k *keyboardInteractiveAuth) method() string { |
| 417 return "keyboard-interactive" |
| 418 } |
| 419 |
| 420 func (k *keyboardInteractiveAuth) auth(session []byte, user string, c packetConn
, rand io.Reader) (bool, []string, error) { |
| 421 type initiateMsg struct { |
| 422 User string |
| 423 Service string |
| 424 Method string |
| 425 Language string |
| 426 Submethods string |
| 427 } |
| 428 |
| 429 if err := c.writePacket(marshal(msgUserAuthRequest, initiateMsg{ |
| 430 User: user, |
| 431 Service: serviceSSH, |
| 432 Method: "keyboard-interactive", |
| 433 })); err != nil { |
| 434 return false, nil, err |
| 435 } |
| 436 |
| 437 for { |
| 438 packet, err := c.readPacket() |
| 439 if err != nil { |
| 440 return false, nil, err |
| 441 } |
| 442 |
| 443 // like handleAuthResponse, but with less options. |
| 444 switch packet[0] { |
| 445 case msgUserAuthBanner: |
| 446 // TODO: Print banners during userauth. |
| 447 continue |
| 448 case msgUserAuthInfoRequest: |
| 449 // OK |
| 450 case msgUserAuthFailure: |
| 451 var msg userAuthFailureMsg |
| 452 if err := unmarshal(&msg, packet, msgUserAuthFailure); e
rr != nil { |
| 453 return false, nil, err |
| 454 } |
| 455 return false, msg.Methods, nil |
| 456 case msgUserAuthSuccess: |
| 457 return true, nil, nil |
| 458 default: |
| 459 return false, nil, UnexpectedMessageError{msgUserAuthInf
oRequest, packet[0]} |
| 460 } |
| 461 |
| 462 var msg userAuthInfoRequestMsg |
| 463 if err := unmarshal(&msg, packet, packet[0]); err != nil { |
| 464 return false, nil, err |
| 465 } |
| 466 |
| 467 // Manually unpack the prompt/echo pairs. |
| 468 rest := msg.Prompts |
| 469 var prompts []string |
| 470 var echos []bool |
| 471 for i := 0; i < int(msg.NumPrompts); i++ { |
| 472 prompt, r, ok := parseString(rest) |
| 473 if !ok || len(r) == 0 { |
| 474 return false, nil, errors.New("ssh: prompt forma
t error") |
| 475 } |
| 476 prompts = append(prompts, string(prompt)) |
| 477 echos = append(echos, r[0] != 0) |
| 478 rest = r[1:] |
| 479 } |
| 480 |
| 481 if len(rest) != 0 { |
| 482 return false, nil, fmt.Errorf("ssh: junk following messa
ge %q", rest) |
| 483 } |
| 484 |
| 485 answers, err := k.Challenge(msg.User, msg.Instruction, prompts,
echos) |
| 486 if err != nil { |
| 487 return false, nil, err |
| 488 } |
| 489 |
| 490 if len(answers) != len(prompts) { |
| 491 return false, nil, errors.New("ssh: not enough answers f
rom keyboard-interactive callback") |
| 492 } |
| 493 responseLength := 1 + 4 |
| 494 for _, a := range answers { |
| 495 responseLength += stringLength(len(a)) |
| 496 } |
| 497 serialized := make([]byte, responseLength) |
| 498 p := serialized |
| 499 p[0] = msgUserAuthInfoResponse |
| 500 p = p[1:] |
| 501 p = marshalUint32(p, uint32(len(answers))) |
| 502 for _, a := range answers { |
| 503 p = marshalString(p, []byte(a)) |
| 504 } |
| 505 |
| 506 if err := c.writePacket(serialized); err != nil { |
| 507 return false, nil, err |
| 508 } |
| 509 } |
| 510 } |
LEFT | RIGHT |