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