Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(623)

Side by Side Diff: ssh/client_auth.go

Issue 5564059: code review 5564059: go.crypto: initial code (Closed)
Patch Set: diff -r b50a7fb49394 https://code.google.com/p/go.crypto Created 12 years, 2 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ssh/client.go ('k') | ssh/client_auth_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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{ser viceUserAuth})); 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 != ni l {
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.transpor t, 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 rem ain")
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.Reade r) (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.R eader) (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 signe d 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 validateKe y
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, use rAuthRequestMsg{
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, erro r) {
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 th e 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 succe eded
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{msgUserAuthSuc cess, packet[0]}
313 }
314 }
315 panic("unreachable")
316 }
OLDNEW
« no previous file with comments | « ssh/client.go ('k') | ssh/client_auth_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b