Mercurial > 510Connectbot
comparison src/ch/ethz/ssh2/auth/AuthenticationManager.java @ 342:175c7d68f3c4
merge ganymed into mainline
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Thu, 31 Jul 2014 16:33:38 -0700 |
parents | a1a2e33b3565 |
children |
comparison
equal
deleted
inserted
replaced
272:ce2f4e397703 | 342:175c7d68f3c4 |
---|---|
1 /* | |
2 * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. | |
3 * Please refer to the LICENSE.txt for licensing details. | |
4 */ | |
5 package ch.ethz.ssh2.auth; | |
6 | |
7 import java.io.IOException; | |
8 import java.io.InterruptedIOException; | |
9 import java.security.KeyPair; | |
10 import java.security.PrivateKey; | |
11 import java.security.SecureRandom; | |
12 import java.security.interfaces.DSAPrivateKey; | |
13 import java.security.interfaces.DSAPublicKey; | |
14 import java.security.interfaces.ECPrivateKey; | |
15 import java.security.interfaces.ECPublicKey; | |
16 import java.security.interfaces.RSAPrivateKey; | |
17 import java.security.interfaces.RSAPublicKey; | |
18 import java.util.HashSet; | |
19 import java.util.Set; | |
20 import java.util.concurrent.ArrayBlockingQueue; | |
21 import java.util.concurrent.BlockingQueue; | |
22 | |
23 import ch.ethz.ssh2.InteractiveCallback; | |
24 import ch.ethz.ssh2.PacketTypeException; | |
25 import ch.ethz.ssh2.crypto.PEMDecoder; | |
26 import ch.ethz.ssh2.packets.PacketServiceAccept; | |
27 import ch.ethz.ssh2.packets.PacketServiceRequest; | |
28 import ch.ethz.ssh2.packets.PacketUserauthBanner; | |
29 import ch.ethz.ssh2.packets.PacketUserauthFailure; | |
30 import ch.ethz.ssh2.packets.PacketUserauthInfoRequest; | |
31 import ch.ethz.ssh2.packets.PacketUserauthInfoResponse; | |
32 import ch.ethz.ssh2.packets.PacketUserauthRequestInteractive; | |
33 import ch.ethz.ssh2.packets.PacketUserauthRequestNone; | |
34 import ch.ethz.ssh2.packets.PacketUserauthRequestPassword; | |
35 import ch.ethz.ssh2.packets.PacketUserauthRequestPublicKey; | |
36 import ch.ethz.ssh2.packets.Packets; | |
37 import ch.ethz.ssh2.packets.TypesWriter; | |
38 import ch.ethz.ssh2.signature.DSASHA1Verify; | |
39 import ch.ethz.ssh2.signature.ECDSASHA2Verify; | |
40 import ch.ethz.ssh2.signature.RSASHA1Verify; | |
41 import ch.ethz.ssh2.transport.ClientTransportManager; | |
42 import ch.ethz.ssh2.transport.MessageHandler; | |
43 | |
44 /** | |
45 * @author Christian Plattner | |
46 * @version $Id: AuthenticationManager.java 161 2014-05-01 18:01:55Z dkocher@sudo.ch $ | |
47 */ | |
48 public class AuthenticationManager implements MessageHandler { | |
49 private ClientTransportManager tm; | |
50 | |
51 private final BlockingQueue<byte[]> packets | |
52 = new ArrayBlockingQueue<byte[]>(5); | |
53 | |
54 private boolean connectionClosed = false; | |
55 | |
56 private String banner; | |
57 | |
58 private Set<String> remainingMethods | |
59 = new HashSet<String>(); | |
60 | |
61 private boolean isPartialSuccess = false; | |
62 | |
63 private boolean authenticated = false; | |
64 private boolean initDone = false; | |
65 | |
66 public AuthenticationManager(ClientTransportManager tm) { | |
67 this.tm = tm; | |
68 } | |
69 | |
70 private byte[] deQueue() throws IOException { | |
71 if (connectionClosed) { | |
72 throw(IOException) new IOException("The connection is closed.").initCause(tm.getReasonClosedCause()); | |
73 } | |
74 | |
75 // Wait for packet | |
76 try { | |
77 return packets.take(); | |
78 } | |
79 catch (InterruptedException e) { | |
80 throw new InterruptedIOException(e.getMessage()); | |
81 } | |
82 } | |
83 | |
84 byte[] getNextMessage() throws IOException { | |
85 while (true) { | |
86 byte[] message = deQueue(); | |
87 | |
88 switch (message[0]) { | |
89 case Packets.SSH_MSG_USERAUTH_BANNER: | |
90 // The server may send an SSH_MSG_USERAUTH_BANNER message at any | |
91 // time after this authentication protocol starts and before | |
92 // authentication is successful. | |
93 PacketUserauthBanner sb = new PacketUserauthBanner(message); | |
94 banner = sb.getBanner(); | |
95 break; | |
96 | |
97 default: | |
98 return message; | |
99 } | |
100 } | |
101 } | |
102 | |
103 public Set<String> getRemainingMethods(String user) throws IOException { | |
104 initialize(user); | |
105 return remainingMethods; | |
106 } | |
107 | |
108 public String getBanner() { | |
109 return banner; | |
110 } | |
111 | |
112 public boolean getPartialSuccess() { | |
113 return isPartialSuccess; | |
114 } | |
115 | |
116 private boolean initialize(String user) throws IOException { | |
117 if (initDone == false) { | |
118 tm.registerMessageHandler(this, 0, 255); | |
119 PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth"); | |
120 tm.sendMessage(sr.getPayload()); | |
121 final PacketServiceAccept accept = new PacketServiceAccept(this.getNextMessage()); | |
122 PacketUserauthRequestNone auth = new PacketUserauthRequestNone("ssh-connection", user); | |
123 tm.sendMessage(auth.getPayload()); | |
124 byte[] message = this.getNextMessage(); | |
125 initDone = true; | |
126 | |
127 switch (message[0]) { | |
128 case Packets.SSH_MSG_USERAUTH_SUCCESS: | |
129 authenticated = true; | |
130 tm.removeMessageHandler(this); | |
131 return true; | |
132 | |
133 case Packets.SSH_MSG_USERAUTH_FAILURE: | |
134 PacketUserauthFailure puf = new PacketUserauthFailure(message); | |
135 remainingMethods = puf.getAuthThatCanContinue(); | |
136 isPartialSuccess = puf.isPartialSuccess(); | |
137 return false; | |
138 } | |
139 | |
140 throw new PacketTypeException(message[0]); | |
141 } | |
142 | |
143 return authenticated; | |
144 } | |
145 | |
146 public boolean authenticatePublicKey(String user, AgentProxy proxy) throws IOException { | |
147 initialize(user); | |
148 boolean success; | |
149 | |
150 for (AgentIdentity identity : proxy.getIdentities()) { | |
151 success = authenticatePublicKey(user, identity); | |
152 | |
153 if (success) { | |
154 return true; | |
155 } | |
156 } | |
157 | |
158 return false; | |
159 } | |
160 | |
161 private boolean authenticatePublicKey(String user, AgentIdentity identity) throws IOException { | |
162 if (!remainingMethods.contains("publickey")) { | |
163 throw new IOException("Authentication method not supported"); | |
164 } | |
165 | |
166 byte[] pubKeyBlob = identity.getPublicKeyBlob(); | |
167 | |
168 if (pubKeyBlob == null) { | |
169 return false; | |
170 } | |
171 | |
172 TypesWriter tw = new TypesWriter(); | |
173 byte[] H = tm.getSessionIdentifier(); | |
174 tw.writeString(H, 0, H.length); | |
175 tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); | |
176 tw.writeString(user); | |
177 tw.writeString("ssh-connection"); | |
178 tw.writeString("publickey"); | |
179 tw.writeBoolean(true); | |
180 tw.writeString(identity.getAlgName()); | |
181 tw.writeString(pubKeyBlob, 0, pubKeyBlob.length); | |
182 byte[] msg = tw.getBytes(); | |
183 byte[] response = identity.sign(msg); | |
184 PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey( | |
185 "ssh-connection", user, identity.getAlgName(), pubKeyBlob, response); | |
186 tm.sendMessage(ua.getPayload()); | |
187 byte[] message = getNextMessage(); | |
188 final int type = message[0]; | |
189 | |
190 switch (type) { | |
191 case Packets.SSH_MSG_USERAUTH_SUCCESS: | |
192 authenticated = true; | |
193 tm.removeMessageHandler(this); | |
194 return true; | |
195 | |
196 case Packets.SSH_MSG_USERAUTH_FAILURE: | |
197 PacketUserauthFailure puf = new PacketUserauthFailure(message); | |
198 remainingMethods = puf.getAuthThatCanContinue(); | |
199 isPartialSuccess = puf.isPartialSuccess(); | |
200 return false; | |
201 } | |
202 | |
203 throw new PacketTypeException(type); | |
204 } | |
205 | |
206 public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd) | |
207 throws IOException { | |
208 KeyPair pair = PEMDecoder.decode(PEMPrivateKey, password); | |
209 return authenticatePublicKey(user, pair, rnd); | |
210 } | |
211 | |
212 public boolean authenticatePublicKey(String user, KeyPair pair, SecureRandom rnd) | |
213 throws IOException { | |
214 PrivateKey key = pair.getPrivate(); | |
215 | |
216 try { | |
217 initialize(user); | |
218 | |
219 if (!remainingMethods.contains("publickey")) { | |
220 throw new IOException("Authentication method publickey not supported by the server at this stage."); | |
221 } | |
222 | |
223 if (key instanceof DSAPrivateKey) { | |
224 DSAPrivateKey pk = (DSAPrivateKey) key; | |
225 byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey((DSAPublicKey) pair.getPublic()); | |
226 TypesWriter tw = new TypesWriter(); | |
227 byte[] H = tm.getSessionIdentifier(); | |
228 tw.writeString(H, 0, H.length); | |
229 tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); | |
230 tw.writeString(user); | |
231 tw.writeString("ssh-connection"); | |
232 tw.writeString("publickey"); | |
233 tw.writeBoolean(true); | |
234 tw.writeString("ssh-dss"); | |
235 tw.writeString(pk_enc, 0, pk_enc.length); | |
236 byte[] msg = tw.getBytes(); | |
237 byte[] ds = DSASHA1Verify.generateSignature(msg, pk, rnd); | |
238 byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds); | |
239 PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user, | |
240 "ssh-dss", pk_enc, ds_enc); | |
241 tm.sendMessage(ua.getPayload()); | |
242 } | |
243 else if (key instanceof RSAPrivateKey) { | |
244 RSAPrivateKey pk = (RSAPrivateKey) key; | |
245 byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey((RSAPublicKey) pair.getPublic()); | |
246 TypesWriter tw = new TypesWriter(); | |
247 { | |
248 byte[] H = tm.getSessionIdentifier(); | |
249 tw.writeString(H, 0, H.length); | |
250 tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); | |
251 tw.writeString(user); | |
252 tw.writeString("ssh-connection"); | |
253 tw.writeString("publickey"); | |
254 tw.writeBoolean(true); | |
255 tw.writeString("ssh-rsa"); | |
256 tw.writeString(pk_enc, 0, pk_enc.length); | |
257 } | |
258 byte[] msg = tw.getBytes(); | |
259 byte[] ds = RSASHA1Verify.generateSignature(msg, pk); | |
260 byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds); | |
261 PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user, | |
262 "ssh-rsa", pk_enc, rsa_sig_enc); | |
263 tm.sendMessage(ua.getPayload()); | |
264 } | |
265 else if (key instanceof ECPrivateKey) { | |
266 ECPrivateKey pk = (ECPrivateKey) key; | |
267 final String algo = ECDSASHA2Verify.ECDSA_SHA2_PREFIX | |
268 + ECDSASHA2Verify.getCurveName(pk.getParams()); | |
269 byte[] pk_enc = ECDSASHA2Verify.encodeSSHECDSAPublicKey((ECPublicKey) pair.getPublic()); | |
270 TypesWriter tw = new TypesWriter(); | |
271 { | |
272 byte[] H = tm.getSessionIdentifier(); | |
273 tw.writeString(H, 0, H.length); | |
274 tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); | |
275 tw.writeString(user); | |
276 tw.writeString("ssh-connection"); | |
277 tw.writeString("publickey"); | |
278 tw.writeBoolean(true); | |
279 tw.writeString(algo); | |
280 tw.writeString(pk_enc, 0, pk_enc.length); | |
281 } | |
282 byte[] msg = tw.getBytes(); | |
283 byte[] ds = ECDSASHA2Verify.generateSignature(msg, pk); | |
284 byte[] ec_sig_enc = ECDSASHA2Verify.encodeSSHECDSASignature(ds, pk.getParams()); | |
285 PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user, | |
286 algo, pk_enc, ec_sig_enc); | |
287 tm.sendMessage(ua.getPayload()); | |
288 } | |
289 else { | |
290 throw new IOException("Unknown private key type returned by the PEM decoder."); | |
291 } | |
292 | |
293 byte[] message = getNextMessage(); | |
294 final int type = message[0]; | |
295 | |
296 switch (type) { | |
297 case Packets.SSH_MSG_USERAUTH_SUCCESS: | |
298 authenticated = true; | |
299 tm.removeMessageHandler(this); | |
300 return true; | |
301 | |
302 case Packets.SSH_MSG_USERAUTH_FAILURE: | |
303 PacketUserauthFailure puf = new PacketUserauthFailure(message); | |
304 remainingMethods = puf.getAuthThatCanContinue(); | |
305 isPartialSuccess = puf.isPartialSuccess(); | |
306 return false; | |
307 } | |
308 | |
309 throw new PacketTypeException(type); | |
310 } | |
311 catch (IOException e) { | |
312 tm.close(e, false); | |
313 throw e; | |
314 } | |
315 } | |
316 | |
317 public boolean authenticateNone(String user) throws IOException { | |
318 try { | |
319 initialize(user); | |
320 return authenticated; | |
321 } | |
322 catch (IOException e) { | |
323 tm.close(e, false); | |
324 throw e; | |
325 } | |
326 } | |
327 | |
328 public boolean authenticatePassword(String user, String pass) throws IOException { | |
329 try { | |
330 initialize(user); | |
331 | |
332 if (!remainingMethods.contains("password")) { | |
333 throw new IOException("Authentication method not supported"); | |
334 } | |
335 | |
336 PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass); | |
337 tm.sendMessage(ua.getPayload()); | |
338 byte[] message = getNextMessage(); | |
339 final int type = message[0]; | |
340 | |
341 switch (type) { | |
342 case Packets.SSH_MSG_USERAUTH_SUCCESS: | |
343 authenticated = true; | |
344 tm.removeMessageHandler(this); | |
345 return true; | |
346 | |
347 case Packets.SSH_MSG_USERAUTH_FAILURE: | |
348 PacketUserauthFailure puf = new PacketUserauthFailure(message); | |
349 remainingMethods = puf.getAuthThatCanContinue(); | |
350 isPartialSuccess = puf.isPartialSuccess(); | |
351 return false; | |
352 } | |
353 | |
354 throw new PacketTypeException(type); | |
355 } | |
356 catch (IOException e) { | |
357 tm.close(e, false); | |
358 throw e; | |
359 } | |
360 } | |
361 | |
362 public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException { | |
363 try { | |
364 initialize(user); | |
365 | |
366 if (!remainingMethods.contains("keyboard-interactive")) { | |
367 throw new IOException( | |
368 "Authentication method keyboard-interactive not supported by the server at this stage."); | |
369 } | |
370 | |
371 PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user, | |
372 submethods); | |
373 tm.sendMessage(ua.getPayload()); | |
374 | |
375 while (true) { | |
376 byte[] message = getNextMessage(); | |
377 final int type = message[0]; | |
378 | |
379 switch (type) { | |
380 case Packets.SSH_MSG_USERAUTH_SUCCESS: | |
381 authenticated = true; | |
382 tm.removeMessageHandler(this); | |
383 return true; | |
384 | |
385 case Packets.SSH_MSG_USERAUTH_FAILURE: | |
386 PacketUserauthFailure puf = new PacketUserauthFailure(message); | |
387 remainingMethods = puf.getAuthThatCanContinue(); | |
388 isPartialSuccess = puf.isPartialSuccess(); | |
389 return false; | |
390 | |
391 case Packets.SSH_MSG_USERAUTH_INFO_REQUEST: | |
392 PacketUserauthInfoRequest info = new PacketUserauthInfoRequest(message); | |
393 String[] responses; | |
394 | |
395 try { | |
396 responses = cb.replyToChallenge(info.getName(), info.getInstruction(), info.getNumPrompts(), | |
397 info.getPrompt(), info.getEcho()); | |
398 } | |
399 catch (Exception e) { | |
400 throw new IOException("Exception in callback.", e); | |
401 } | |
402 | |
403 PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses); | |
404 tm.sendMessage(puir.getPayload()); | |
405 continue; | |
406 } | |
407 | |
408 throw new PacketTypeException(type); | |
409 } | |
410 } | |
411 catch (IOException e) { | |
412 tm.close(e, false); | |
413 throw e; | |
414 } | |
415 } | |
416 | |
417 public void handleFailure(final IOException failure) { | |
418 connectionClosed = true; | |
419 } | |
420 | |
421 public void handleMessage(byte[] message) throws IOException { | |
422 packets.add(message); | |
423 } | |
424 } |