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 }