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