source: other-projects/trunk/gs3-release-maker/tasks/sshtaskdef/src/mindbright/ssh/SSHServer.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 12.6 KB
Line 
1/******************************************************************************
2 *
3 * Copyright (c) 1998,99 by Mindbright Technology AB, Stockholm, Sweden.
4 * www.mindbright.se, [email protected]
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 *****************************************************************************
17 * $Author: mats $
18 * $Date: 2000/03/28 13:48:49 $
19 * $Name: rel1-2-1 $
20 *****************************************************************************/
21package mindbright.ssh;
22
23import java.net.*;
24import java.io.*;
25import java.math.BigInteger;
26
27import mindbright.security.*;
28import mindbright.terminal.*;
29
30/**
31 * @author Mats Andersson
32 * @version 0.96, 12/04/98
33 * @see SSHClient
34 */
35public class SSHServer extends SSH implements Runnable {
36
37 static KeyPair serverKey;
38 static KeyPair hostKey;
39
40 protected InetAddress localAddr;
41
42 static String authKeysDir = "";
43 static String hostKeyFile = "identity";
44 static int serverKeyBits = SSH.SERVER_KEY_LENGTH;
45
46 protected String cliVersionStr;
47 protected int cliVersionMajor;
48 protected int cliVersionMinor;
49
50 protected Thread myThread;
51
52 protected Socket sshSocket;
53 protected BufferedInputStream sshIn;
54 protected BufferedOutputStream sshOut;
55
56 protected SSHChannelController controller;
57
58 static public void setHostKeyFile(String fileName) {
59 hostKeyFile = fileName;
60 }
61
62 static public void setAuthKeysDir(String dirName) {
63 authKeysDir = dirName;
64 }
65
66 static public void setServerKeyBits(int bits) {
67 serverKeyBits = bits;
68 }
69
70 public InetAddress getLocalAddr() {
71 return localAddr;
72 }
73
74 public void setLocalAddr(String addr) throws UnknownHostException {
75 localAddr = InetAddress.getByName(addr);
76 }
77
78 public SSHServer(Socket sshSocket,
79 int protocolFlags, int supportedCiphers, int supportedAuthTypes,
80 KeyPair srvServerKey, KeyPair srvHostKey) throws IOException {
81 this.isAnSSHClient = false;
82 this.sshSocket = sshSocket;
83 this.sshIn = new BufferedInputStream(sshSocket.getInputStream(), 8192);
84 this.sshOut = new BufferedOutputStream(sshSocket.getOutputStream());
85
86 this.protocolFlags = protocolFlags;
87 this.supportedCiphers = supportedCiphers;
88 this.supportedAuthTypes = supportedAuthTypes;
89 this.srvServerKey = srvServerKey;
90 this.srvHostKey = srvHostKey;
91 }
92
93 protected void start() {
94 myThread = new Thread(this);
95 myThread.start();
96 }
97
98 public void run() {
99 try {
100 System.out.println("connection from " + sshSocket.getInetAddress().getHostAddress() + " port " +
101 sshSocket.getPort());
102
103 negotiateVersion();
104 sendServerData();
105 receiveSessionKey();
106
107 // !!!
108 // At this stage the communication is encrypted
109 // !!!
110
111 authenticateUser();
112 controller = new SSHChannelController(this, sshIn, sshOut, sndCipher, rcvCipher,
113 null, true);
114 receiveOptions();
115
116 controller.start();
117
118 try {
119 controller.waitForExit();
120 } catch(InterruptedException e) {
121 // !!!
122 log("Error when shutting down SSHClient: " + e.getMessage());
123 controller.killAll();
124 }
125
126 } catch (IOException e) {
127 // !!!
128 log("error in MindTunnel: " + e);
129 }
130 }
131
132 static RSAPrivateKey getPrivate(SSHRSAKeyFile keyFile) {
133 RSAPrivateKey privKey = keyFile.getPrivate("");
134 while(privKey == null) {
135 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
136 System.out.println("");
137 System.out.print("key-file '" + keyFile.getComment() + "' password: ");
138 String passwd;
139 try {
140 passwd = br.readLine();
141 } catch(IOException e) {
142 passwd = "";
143 }
144 privKey = keyFile.getPrivate(passwd);
145 }
146 return privKey;
147 }
148
149 public static void sshd(int port) throws IOException {
150 boolean keepRunning = true;
151 ServerSocket listenSock = null;
152
153 SSHRSAKeyFile keyFile;
154 keyFile = new SSHRSAKeyFile(hostKeyFile);
155 hostKey = new KeyPair(keyFile.getPublic(), getPrivate(keyFile));
156 listenSock = new ServerSocket(port);
157
158 int keyLenDiff = Math.abs(serverKeyBits -
159 ((RSAPublicKey)hostKey.getPublic()).bitLength());
160 if(keyLenDiff < 24) {
161 throw new IOException("Invalid server keys, difference in sizes must be at least 24 bits");
162 }
163
164 System.out.print("generating server-key of length " + serverKeyBits + "...");
165 serverKey = SSH.generateRSAKeyPair(serverKeyBits, secureRandom());
166 System.out.println("done");
167
168 System.out.println("starting new MindTunnel on port " + port + "...");
169
170 while(keepRunning) {
171 Socket sshSocket = listenSock.accept();
172 SSHServer srv = new SSHServer(sshSocket, PROTOFLAG_HOST_IN_FWD_OPEN, 0xff, 0x0f,
173 serverKey, hostKey);
174
175 // !!! Change later, must take arguments for localHost
176 //
177 srv.localAddr = InetAddress.getLocalHost();
178 srv.start();
179 }
180
181 }
182
183 void negotiateVersion() throws IOException {
184 byte[] buf;
185 int len;
186 String verStr;
187
188 verStr = getVersionId(false);
189 verStr += "\n";
190 buf = verStr.getBytes();
191
192 sshOut.write(buf);
193 sshOut.flush();
194
195 buf = new byte[256];
196 len = sshIn.read(buf);
197
198 cliVersionStr = new String(buf, 0, len);
199
200 try {
201 int l = cliVersionStr.indexOf('-');
202 int r = cliVersionStr.indexOf('.');
203 cliVersionMajor = Integer.parseInt(cliVersionStr.substring(l + 1, r));
204 l = r;
205 r = cliVersionStr.indexOf('-', l);
206 if(r == -1) {
207 cliVersionMinor = Integer.parseInt(cliVersionStr.substring(l + 1));
208 } else {
209 cliVersionMinor = Integer.parseInt(cliVersionStr.substring(l + 1, r));
210 }
211 } catch (Throwable t) {
212 throw new IOException("Client version string invalid: " + cliVersionStr);
213 }
214
215 if(cliVersionMajor > 1) {
216 throw new IOException("MindTunnel do not support SSHv2 yet, can only serve SSHv1 client");
217 } else if(cliVersionMajor < 1 || cliVersionMinor < 5) {
218 throw new IOException("Client's protocol version (" + cliVersionMajor + "-" +
219 cliVersionMinor + ") is too old, please upgrade");
220 }
221
222 // Strip white-space
223 cliVersionStr = cliVersionStr.trim();
224 }
225
226 void sendServerData() throws IOException {
227 SSHPduOutputStream pdu = new SSHPduOutputStream(SMSG_PUBLIC_KEY, null);
228
229 SecureRandom rand = secureRandom();
230
231 srvCookie = new byte[8];
232 rand.nextBytes(srvCookie);
233 generateSessionId();
234
235 pdu.write(srvCookie);
236
237 RSAPublicKey publ = (RSAPublicKey)srvServerKey.getPublic();
238 int n = publ.bitLength();
239 pdu.writeInt(n);
240 pdu.writeBigInteger(publ.getE());
241 pdu.writeBigInteger(publ.getN());
242
243 publ = (RSAPublicKey)srvHostKey.getPublic();
244 n = publ.bitLength();
245 pdu.writeInt(n);
246 pdu.writeBigInteger(publ.getE());
247 pdu.writeBigInteger(publ.getN());
248
249 pdu.writeInt(protocolFlags);
250 pdu.writeInt(supportedCiphers);
251 pdu.writeInt(supportedAuthTypes);
252
253 pdu.writeTo(sshOut);
254 }
255
256 void receiveSessionKey() throws IOException {
257 SSHPduInputStream inpdu = new SSHPduInputStream(CMSG_SESSION_KEY, null);
258 inpdu.readFrom(sshIn);
259
260 cipherType = (int)inpdu.readByte();
261 if(!isCipherSupported(cipherType))
262 //sendDisconnect();
263 ;
264 log("cipher: " + getCipherName(cipherType));
265
266 byte[] srvCookieCopy = new byte[srvCookie.length];
267 inpdu.read(srvCookieCopy);
268
269 BigInteger encKey = inpdu.readBigInteger();
270 int cliProtoFlags = inpdu.readInt();
271
272 RSACipher rsa1;
273 RSACipher rsa2;
274 if(((RSAPrivateKey)serverKey.getPrivate()).bitLength() >
275 ((RSAPrivateKey)hostKey.getPrivate()).bitLength()) {
276 rsa1 = new RSACipher(serverKey);
277 rsa2 = new RSACipher(hostKey);
278 } else {
279 rsa2 = new RSACipher(serverKey);
280 rsa1 = new RSACipher(hostKey);
281 }
282 encKey = rsa1.doPrivate(encKey);
283 encKey = rsa1.stripPad(encKey);
284 encKey = rsa2.doPrivate(encKey);
285 encKey = rsa2.stripPad(encKey);
286 sessionKey = encKey.toByteArray();
287
288 // Must strip if too long, i.e. m.s.b. is 0 because of how BigInteger's toByteArray work...
289 //
290 if(sessionKey.length > (SESSION_KEY_LENGTH / 8)) {
291 byte[] keyCopy = new byte[SESSION_KEY_LENGTH / 8];
292 System.arraycopy(sessionKey, 1, keyCopy, 0, SESSION_KEY_LENGTH / 8);
293 sessionKey = keyCopy;
294 }
295
296 for(int i = 0; i < sessionId.length; i++)
297 sessionKey[i] ^= sessionId[i];
298
299 initServerCipher();
300
301 // !!! Check that all is ok...
302
303 sendResult(SMSG_SUCCESS);
304 }
305
306 void authenticateUser() throws IOException {
307 boolean finished = false;
308 SSHPduInputStream inpdu = new SSHPduInputStream(CMSG_USER, rcvCipher);
309 String user;
310
311 inpdu.readFrom(sshIn);
312 user = inpdu.readString();
313
314 log("authenticating: " + user);
315 sendResult(SMSG_FAILURE);
316
317 while(!finished) {
318 inpdu = new SSHPduInputStream(MSG_ANY, rcvCipher);
319 inpdu.readFrom(sshIn);
320
321 switch(inpdu.type) {
322 case CMSG_AUTH_RSA:
323 if(doRSAAuth(user, inpdu.readBigInteger())) {
324 log("rsa-authentication for " + user + " succeeded");
325 sendResult(SMSG_SUCCESS);
326 finished = true;
327 } else {
328 log("rsa-authentication for " + user + " failed");
329 sendResult(SMSG_FAILURE);
330 }
331 break;
332 case CMSG_AUTH_PASSWORD:
333 log("trying passwd-auth for: " + user);
334 sendResult(SMSG_FAILURE);
335 break;
336 default:
337 sendResult(SMSG_FAILURE);
338 }
339 }
340
341 }
342
343 boolean doRSAAuth(String userName, BigInteger pubKeyN) throws IOException {
344 SSHRSAPublicKeyFile keyFile;
345 SSHPduOutputStream outpdu;
346 SSHPduInputStream inpdu;
347 boolean authenticated = false;
348
349 keyFile = SSHRSAPublicKeyFile.loadFromFile(authKeysDir + userName, false);
350
351 RSAPublicKey pubKey = keyFile.getPublic(pubKeyN, userName);
352 if(pubKey == null)
353 return false;
354
355 RSACipher rsa = new RSACipher(new KeyPair(pubKey, null));
356 byte[] challenge = new byte[32];
357 byte[] tmp;
358 BigInteger enc;
359
360 secureRandom().nextBytes(challenge);
361 tmp = new byte[challenge.length + 1];
362 System.arraycopy(challenge, 0, tmp, 1, challenge.length);
363 enc = new BigInteger(tmp);
364
365 enc = rsa.doPad(enc, pubKey.bitLength(), secureRandom());
366 enc = rsa.doPublic(enc);
367 outpdu = new SSHPduOutputStream(SMSG_AUTH_RSA_CHALLENGE, sndCipher);
368 outpdu.writeBigInteger(enc);
369 outpdu.writeTo(sshOut);
370
371 inpdu = new SSHPduInputStream(CMSG_AUTH_RSA_RESPONSE, rcvCipher);
372 inpdu.readFrom(sshIn);
373 tmp = new byte[16];
374 inpdu.read(tmp, 0, 16);
375
376 try {
377 MessageDigest md5 = MessageDigest.getInstance("MD5");
378 md5.update(challenge, 0, 32);
379 md5.update(sessionId);
380 challenge = md5.digest();
381 } catch (Exception e) {
382 // !!!
383 System.out.println("!!! MD5 Not supported...");
384 throw new IOException(e.getMessage());
385 }
386
387 int i;
388 for(i = 0; i < challenge.length; i++)
389 if(tmp[i] != challenge[i])
390 break;
391 if(i == challenge.length)
392 authenticated = true;
393
394 return authenticated;
395 }
396
397 void receiveOptions() throws IOException {
398 SSHPduInputStream pdu;
399 boolean finished = false;
400
401 while(!finished) {
402 pdu = new SSHPduInputStream(MSG_ANY, rcvCipher);
403 pdu.readFrom(sshIn);
404 switch(pdu.type) {
405 case CMSG_REQUEST_COMPRESSION:
406 log("compression requested");
407 break;
408 case CMSG_MAX_PACKET_SIZE:
409 log("mtu requested");
410 break;
411 case CMSG_X11_REQUEST_FORWARDING:
412 log("x11-tunnel requested");
413 sendResult(SMSG_FAILURE);
414 break;
415 case CMSG_REQUEST_PTY:
416 log("pty requested");
417 sendResult(SMSG_FAILURE);
418 break;
419 case CMSG_PORT_FORWARD_REQUEST:
420 int localPort = pdu.readInt();
421 String remoteHost = pdu.readString();
422 int remotePort = pdu.readInt();
423 log("port-fwd requested: " + localPort + ":" + remoteHost + ":" + remotePort);
424 controller.newListenChannel(localAddr.getHostAddress(), localPort, remoteHost, remotePort, "general");
425 sendResult(SMSG_SUCCESS);
426 break;
427 case CMSG_EXEC_CMD:
428 String command = pdu.readString();
429 log("cmd: " + command);
430 finished = true;
431 break;
432 case CMSG_EXEC_SHELL:
433 log("exec-shell");
434 finished = true;
435 break;
436 default:
437 log("receiveOptions got unknown msg");
438 break;
439 }
440 }
441 }
442
443 void sendResult(int type) throws IOException {
444 SSHPduOutputStream pdu;
445 pdu = new SSHPduOutputStream(type, sndCipher);
446 pdu.writeTo(sshOut);
447 }
448
449}
Note: See TracBrowser for help on using the repository browser.