Mercurial > 510Connectbot
comparison src/com/trilead/ssh2/auth/AuthenticationManager.java @ 0:0ce5cc452d02
initial version
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Thu, 22 May 2014 10:41:19 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:0ce5cc452d02 |
---|---|
1 | |
2 package com.trilead.ssh2.auth; | |
3 | |
4 import java.io.IOException; | |
5 import java.security.KeyPair; | |
6 import java.security.PrivateKey; | |
7 import java.security.SecureRandom; | |
8 import java.security.interfaces.DSAPrivateKey; | |
9 import java.security.interfaces.DSAPublicKey; | |
10 import java.security.interfaces.ECPrivateKey; | |
11 import java.security.interfaces.ECPublicKey; | |
12 import java.security.interfaces.RSAPrivateKey; | |
13 import java.security.interfaces.RSAPublicKey; | |
14 import java.util.Vector; | |
15 | |
16 import com.trilead.ssh2.InteractiveCallback; | |
17 import com.trilead.ssh2.crypto.PEMDecoder; | |
18 import com.trilead.ssh2.packets.PacketServiceAccept; | |
19 import com.trilead.ssh2.packets.PacketServiceRequest; | |
20 import com.trilead.ssh2.packets.PacketUserauthBanner; | |
21 import com.trilead.ssh2.packets.PacketUserauthFailure; | |
22 import com.trilead.ssh2.packets.PacketUserauthInfoRequest; | |
23 import com.trilead.ssh2.packets.PacketUserauthInfoResponse; | |
24 import com.trilead.ssh2.packets.PacketUserauthRequestInteractive; | |
25 import com.trilead.ssh2.packets.PacketUserauthRequestNone; | |
26 import com.trilead.ssh2.packets.PacketUserauthRequestPassword; | |
27 import com.trilead.ssh2.packets.PacketUserauthRequestPublicKey; | |
28 import com.trilead.ssh2.packets.Packets; | |
29 import com.trilead.ssh2.packets.TypesWriter; | |
30 import com.trilead.ssh2.signature.DSASHA1Verify; | |
31 import com.trilead.ssh2.signature.ECDSASHA2Verify; | |
32 import com.trilead.ssh2.signature.RSASHA1Verify; | |
33 import com.trilead.ssh2.transport.MessageHandler; | |
34 import com.trilead.ssh2.transport.TransportManager; | |
35 | |
36 | |
37 /** | |
38 * AuthenticationManager. | |
39 * | |
40 * @author Christian Plattner, plattner@trilead.com | |
41 * @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ | |
42 */ | |
43 public class AuthenticationManager implements MessageHandler { | |
44 TransportManager tm; | |
45 | |
46 Vector packets = new Vector(); | |
47 boolean connectionClosed = false; | |
48 | |
49 String banner; | |
50 | |
51 String[] remainingMethods = new String[0]; | |
52 boolean isPartialSuccess = false; | |
53 | |
54 boolean authenticated = false; | |
55 boolean initDone = false; | |
56 | |
57 public AuthenticationManager(TransportManager tm) { | |
58 this.tm = tm; | |
59 } | |
60 | |
61 boolean methodPossible(String methName) { | |
62 if (remainingMethods == null) | |
63 return false; | |
64 | |
65 for (int i = 0; i < remainingMethods.length; i++) { | |
66 if (remainingMethods[i].compareTo(methName) == 0) | |
67 return true; | |
68 } | |
69 | |
70 return false; | |
71 } | |
72 | |
73 byte[] deQueue() throws IOException { | |
74 synchronized (packets) { | |
75 while (packets.size() == 0) { | |
76 if (connectionClosed) | |
77 throw(IOException) new IOException("The connection is closed.").initCause(tm | |
78 .getReasonClosedCause()); | |
79 | |
80 try { | |
81 packets.wait(); | |
82 } | |
83 catch (InterruptedException ign) { | |
84 } | |
85 } | |
86 | |
87 /* This sequence works with J2ME */ | |
88 byte[] res = (byte[]) packets.firstElement(); | |
89 packets.removeElementAt(0); | |
90 return res; | |
91 } | |
92 } | |
93 | |
94 byte[] getNextMessage() throws IOException { | |
95 while (true) { | |
96 byte[] msg = deQueue(); | |
97 | |
98 if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER) | |
99 return msg; | |
100 | |
101 PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length); | |
102 banner = sb.getBanner(); | |
103 } | |
104 } | |
105 | |
106 public String[] getRemainingMethods(String user) throws IOException { | |
107 initialize(user); | |
108 return remainingMethods; | |
109 } | |
110 | |
111 public boolean getPartialSuccess() { | |
112 return isPartialSuccess; | |
113 } | |
114 | |
115 private boolean initialize(String user) throws IOException { | |
116 if (initDone == false) { | |
117 tm.registerMessageHandler(this, 0, 255); | |
118 PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth"); | |
119 tm.sendMessage(sr.getPayload()); | |
120 PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user); | |
121 tm.sendMessage(urn.getPayload()); | |
122 byte[] msg = getNextMessage(); | |
123 new PacketServiceAccept(msg, 0, msg.length); | |
124 msg = getNextMessage(); | |
125 initDone = true; | |
126 | |
127 if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) { | |
128 authenticated = true; | |
129 tm.removeMessageHandler(this, 0, 255); | |
130 return true; | |
131 } | |
132 | |
133 if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE) { | |
134 PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length); | |
135 remainingMethods = puf.getAuthThatCanContinue(); | |
136 isPartialSuccess = puf.isPartialSuccess(); | |
137 return false; | |
138 } | |
139 | |
140 throw new IOException("Unexpected SSH message (type " + msg[0] + ")"); | |
141 } | |
142 | |
143 return authenticated; | |
144 } | |
145 | |
146 public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd) | |
147 throws IOException { | |
148 KeyPair pair = PEMDecoder.decode(PEMPrivateKey, password); | |
149 return authenticatePublicKey(user, pair, rnd); | |
150 } | |
151 | |
152 public boolean authenticatePublicKey(String user, KeyPair pair, SecureRandom rnd) | |
153 throws IOException { | |
154 PrivateKey key = pair.getPrivate(); | |
155 | |
156 try { | |
157 initialize(user); | |
158 | |
159 if (methodPossible("publickey") == false) | |
160 throw new IOException("Authentication method publickey not supported by the server at this stage."); | |
161 | |
162 if (key instanceof DSAPrivateKey) { | |
163 DSAPrivateKey pk = (DSAPrivateKey) key; | |
164 byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey((DSAPublicKey) pair.getPublic()); | |
165 TypesWriter tw = new TypesWriter(); | |
166 byte[] H = tm.getSessionIdentifier(); | |
167 tw.writeString(H, 0, H.length); | |
168 tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); | |
169 tw.writeString(user); | |
170 tw.writeString("ssh-connection"); | |
171 tw.writeString("publickey"); | |
172 tw.writeBoolean(true); | |
173 tw.writeString("ssh-dss"); | |
174 tw.writeString(pk_enc, 0, pk_enc.length); | |
175 byte[] msg = tw.getBytes(); | |
176 byte[] ds = DSASHA1Verify.generateSignature(msg, pk, rnd); | |
177 byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds); | |
178 PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user, | |
179 "ssh-dss", pk_enc, ds_enc); | |
180 tm.sendMessage(ua.getPayload()); | |
181 } | |
182 else if (key instanceof RSAPrivateKey) { | |
183 RSAPrivateKey pk = (RSAPrivateKey) key; | |
184 byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey((RSAPublicKey) pair.getPublic()); | |
185 TypesWriter tw = new TypesWriter(); | |
186 { | |
187 byte[] H = tm.getSessionIdentifier(); | |
188 tw.writeString(H, 0, H.length); | |
189 tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); | |
190 tw.writeString(user); | |
191 tw.writeString("ssh-connection"); | |
192 tw.writeString("publickey"); | |
193 tw.writeBoolean(true); | |
194 tw.writeString("ssh-rsa"); | |
195 tw.writeString(pk_enc, 0, pk_enc.length); | |
196 } | |
197 byte[] msg = tw.getBytes(); | |
198 byte[] ds = RSASHA1Verify.generateSignature(msg, pk); | |
199 byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds); | |
200 PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user, | |
201 "ssh-rsa", pk_enc, rsa_sig_enc); | |
202 tm.sendMessage(ua.getPayload()); | |
203 } | |
204 else if (key instanceof ECPrivateKey) { | |
205 ECPrivateKey pk = (ECPrivateKey) key; | |
206 final String algo = ECDSASHA2Verify.ECDSA_SHA2_PREFIX | |
207 + ECDSASHA2Verify.getCurveName(pk.getParams()); | |
208 byte[] pk_enc = ECDSASHA2Verify.encodeSSHECDSAPublicKey((ECPublicKey) pair.getPublic()); | |
209 TypesWriter tw = new TypesWriter(); | |
210 { | |
211 byte[] H = tm.getSessionIdentifier(); | |
212 tw.writeString(H, 0, H.length); | |
213 tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); | |
214 tw.writeString(user); | |
215 tw.writeString("ssh-connection"); | |
216 tw.writeString("publickey"); | |
217 tw.writeBoolean(true); | |
218 tw.writeString(algo); | |
219 tw.writeString(pk_enc, 0, pk_enc.length); | |
220 } | |
221 byte[] msg = tw.getBytes(); | |
222 byte[] ds = ECDSASHA2Verify.generateSignature(msg, pk); | |
223 byte[] ec_sig_enc = ECDSASHA2Verify.encodeSSHECDSASignature(ds, pk.getParams()); | |
224 PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user, | |
225 algo, pk_enc, ec_sig_enc); | |
226 tm.sendMessage(ua.getPayload()); | |
227 } | |
228 else { | |
229 throw new IOException("Unknown private key type returned by the PEM decoder."); | |
230 } | |
231 | |
232 byte[] ar = getNextMessage(); | |
233 | |
234 if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) { | |
235 authenticated = true; | |
236 tm.removeMessageHandler(this, 0, 255); | |
237 return true; | |
238 } | |
239 | |
240 if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) { | |
241 PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length); | |
242 remainingMethods = puf.getAuthThatCanContinue(); | |
243 isPartialSuccess = puf.isPartialSuccess(); | |
244 return false; | |
245 } | |
246 | |
247 throw new IOException("Unexpected SSH message (type " + ar[0] + ")"); | |
248 } | |
249 catch (IOException e) { | |
250 e.printStackTrace(); | |
251 tm.close(e, false); | |
252 throw(IOException) new IOException("Publickey authentication failed.").initCause(e); | |
253 } | |
254 } | |
255 | |
256 public boolean authenticateNone(String user) throws IOException { | |
257 try { | |
258 initialize(user); | |
259 return authenticated; | |
260 } | |
261 catch (IOException e) { | |
262 tm.close(e, false); | |
263 throw(IOException) new IOException("None authentication failed.").initCause(e); | |
264 } | |
265 } | |
266 | |
267 public boolean authenticatePassword(String user, String pass) throws IOException { | |
268 try { | |
269 initialize(user); | |
270 | |
271 if (methodPossible("password") == false) | |
272 throw new IOException("Authentication method password not supported by the server at this stage."); | |
273 | |
274 PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass); | |
275 tm.sendMessage(ua.getPayload()); | |
276 byte[] ar = getNextMessage(); | |
277 | |
278 if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) { | |
279 authenticated = true; | |
280 tm.removeMessageHandler(this, 0, 255); | |
281 return true; | |
282 } | |
283 | |
284 if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) { | |
285 PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length); | |
286 remainingMethods = puf.getAuthThatCanContinue(); | |
287 isPartialSuccess = puf.isPartialSuccess(); | |
288 return false; | |
289 } | |
290 | |
291 throw new IOException("Unexpected SSH message (type " + ar[0] + ")"); | |
292 } | |
293 catch (IOException e) { | |
294 tm.close(e, false); | |
295 throw(IOException) new IOException("Password authentication failed.").initCause(e); | |
296 } | |
297 } | |
298 | |
299 public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException { | |
300 try { | |
301 initialize(user); | |
302 | |
303 if (methodPossible("keyboard-interactive") == false) | |
304 throw new IOException( | |
305 "Authentication method keyboard-interactive not supported by the server at this stage."); | |
306 | |
307 if (submethods == null) | |
308 submethods = new String[0]; | |
309 | |
310 PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user, | |
311 submethods); | |
312 tm.sendMessage(ua.getPayload()); | |
313 | |
314 while (true) { | |
315 byte[] ar = getNextMessage(); | |
316 | |
317 if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS) { | |
318 authenticated = true; | |
319 tm.removeMessageHandler(this, 0, 255); | |
320 return true; | |
321 } | |
322 | |
323 if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE) { | |
324 PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length); | |
325 remainingMethods = puf.getAuthThatCanContinue(); | |
326 isPartialSuccess = puf.isPartialSuccess(); | |
327 return false; | |
328 } | |
329 | |
330 if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST) { | |
331 PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length); | |
332 String[] responses; | |
333 | |
334 try { | |
335 responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui | |
336 .getPrompt(), pui.getEcho()); | |
337 } | |
338 catch (Exception e) { | |
339 throw(IOException) new IOException("Exception in callback.").initCause(e); | |
340 } | |
341 | |
342 if (responses == null) | |
343 throw new IOException("Your callback may not return NULL!"); | |
344 | |
345 PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses); | |
346 tm.sendMessage(puir.getPayload()); | |
347 continue; | |
348 } | |
349 | |
350 throw new IOException("Unexpected SSH message (type " + ar[0] + ")"); | |
351 } | |
352 } | |
353 catch (IOException e) { | |
354 tm.close(e, false); | |
355 throw(IOException) new IOException("Keyboard-interactive authentication failed.").initCause(e); | |
356 } | |
357 } | |
358 | |
359 public void handleMessage(byte[] msg, int msglen) throws IOException { | |
360 synchronized (packets) { | |
361 if (msg == null) { | |
362 connectionClosed = true; | |
363 } | |
364 else { | |
365 byte[] tmp = new byte[msglen]; | |
366 System.arraycopy(msg, 0, tmp, 0, msglen); | |
367 packets.addElement(tmp); | |
368 } | |
369 | |
370 packets.notifyAll(); | |
371 | |
372 if (packets.size() > 5) { | |
373 connectionClosed = true; | |
374 throw new IOException("Error, peer is flooding us with authentication packets."); | |
375 } | |
376 } | |
377 } | |
378 } |