001: /*
002: * SSHTools - Java SSH2 API
003: *
004: * Copyright (C) 2002-2003 Lee David Painter and Contributors.
005: *
006: * Contributions made by:
007: *
008: * Brett Smith
009: * Richard Pernavas
010: * Erwin Bolwidt
011: *
012: * This program is free software; you can redistribute it and/or
013: * modify it under the terms of the GNU General Public License
014: * as published by the Free Software Foundation; either version 2
015: * of the License, or (at your option) any later version.
016: *
017: * This program is distributed in the hope that it will be useful,
018: * but WITHOUT ANY WARRANTY; without even the implied warranty of
019: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
020: * GNU General Public License for more details.
021: *
022: * You should have received a copy of the GNU General Public License
023: * along with this program; if not, write to the Free Software
024: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
025: */
026: package com.sshtools.daemon.transport;
027:
028: import com.sshtools.daemon.configuration.ServerConfiguration;
029:
030: import com.sshtools.j2ssh.configuration.ConfigurationLoader;
031: import com.sshtools.j2ssh.transport.*;
032: import com.sshtools.j2ssh.transport.cipher.SshCipher;
033: import com.sshtools.j2ssh.transport.cipher.SshCipherFactory;
034: import com.sshtools.j2ssh.transport.hmac.SshHmac;
035: import com.sshtools.j2ssh.transport.hmac.SshHmacFactory;
036: import com.sshtools.j2ssh.transport.kex.KeyExchangeException;
037: import com.sshtools.j2ssh.transport.kex.SshKeyExchange;
038: import com.sshtools.j2ssh.transport.publickey.SshKeyPairFactory;
039: import com.sshtools.j2ssh.transport.publickey.SshPrivateKey;
040:
041: import org.apache.commons.logging.Log;
042: import org.apache.commons.logging.LogFactory;
043:
044: import java.io.IOException;
045:
046: import java.util.ArrayList;
047: import java.util.HashMap;
048: import java.util.Iterator;
049: import java.util.List;
050: import java.util.Map;
051:
052: /**
053: *
054: *
055: * @author $author$
056: * @version $Revision: 1.12 $
057: */
058: public class TransportProtocolServer extends TransportProtocolCommon {
059: private static Log log = LogFactory
060: .getLog(TransportProtocolServer.class);
061: private Map acceptServices = new HashMap();
062: private ServerConfiguration config;
063: private boolean refuse = false;
064:
065: /**
066: * Creates a new TransportProtocolServer object.
067: *
068: * @throws IOException
069: */
070: public TransportProtocolServer() throws IOException {
071: config = (ServerConfiguration) ConfigurationLoader
072: .getConfiguration(ServerConfiguration.class);
073: }
074:
075: /**
076: * Creates a new TransportProtocolServer object.
077: *
078: * @param refuse
079: *
080: * @throws IOException
081: */
082: public TransportProtocolServer(boolean refuse) throws IOException {
083: this ();
084: this .refuse = refuse;
085: }
086:
087: /**
088: *
089: */
090: protected void onDisconnect() {
091: acceptServices.clear();
092: }
093:
094: /**
095: *
096: *
097: * @param service
098: *
099: * @throws IOException
100: */
101: public void acceptService(Service service) throws IOException {
102: acceptServices.put(service.getServiceName(), service);
103: }
104:
105: /**
106: *
107: *
108: * @throws IOException
109: */
110: public void refuseConnection() throws IOException {
111: log.info("Refusing connection");
112:
113: // disconnect with max_connections reason
114: sendDisconnect(SshMsgDisconnect.TOO_MANY_CONNECTIONS,
115: "Too many connections");
116: }
117:
118: /**
119: *
120: *
121: * @throws MessageAlreadyRegisteredException
122: */
123: public void registerTransportMessages()
124: throws MessageAlreadyRegisteredException {
125: messageStore.registerMessage(
126: SshMsgServiceRequest.SSH_MSG_SERVICE_REQUEST,
127: SshMsgServiceRequest.class);
128: }
129:
130: /**
131: *
132: *
133: * @throws IOException
134: */
135: protected void startBinaryPacketProtocol() throws IOException {
136: if (refuse) {
137: sendKeyExchangeInit();
138:
139: //sshIn.open();
140: refuseConnection();
141: } else {
142: super .startBinaryPacketProtocol();
143: }
144: }
145:
146: /**
147: *
148: *
149: * @return
150: *
151: * @throws AlgorithmNotAgreedException
152: */
153: protected String getDecryptionAlgorithm()
154: throws AlgorithmNotAgreedException {
155: return determineAlgorithm(clientKexInit
156: .getSupportedCSEncryption(), serverKexInit
157: .getSupportedCSEncryption());
158: }
159:
160: /**
161: *
162: *
163: * @return
164: *
165: * @throws AlgorithmNotAgreedException
166: */
167: protected String getEncryptionAlgorithm()
168: throws AlgorithmNotAgreedException {
169: return determineAlgorithm(clientKexInit
170: .getSupportedSCEncryption(), serverKexInit
171: .getSupportedSCEncryption());
172: }
173:
174: /**
175: *
176: *
177: * @return
178: *
179: * @throws AlgorithmNotAgreedException
180: */
181: protected String getInputStreamCompAlgortihm()
182: throws AlgorithmNotAgreedException {
183: return determineAlgorithm(clientKexInit.getSupportedCSComp(),
184: serverKexInit.getSupportedCSComp());
185: }
186:
187: /**
188: *
189: *
190: * @return
191: *
192: * @throws AlgorithmNotAgreedException
193: */
194: protected String getInputStreamMacAlgorithm()
195: throws AlgorithmNotAgreedException {
196: return determineAlgorithm(clientKexInit.getSupportedCSMac(),
197: serverKexInit.getSupportedCSMac());
198: }
199:
200: /**
201: *
202: */
203: protected void setLocalIdent() {
204: serverIdent = "SSH-" + PROTOCOL_VERSION + "-"
205: + SOFTWARE_VERSION_COMMENTS + " [SERVER]";
206: }
207:
208: /**
209: *
210: *
211: * @return
212: */
213: public String getLocalId() {
214: return serverIdent;
215: }
216:
217: /**
218: *
219: *
220: * @param msg
221: */
222: protected void setLocalKexInit(SshMsgKexInit msg) {
223: log.debug(msg.toString());
224: serverKexInit = msg;
225: }
226:
227: /**
228: *
229: *
230: * @return
231: */
232: protected SshMsgKexInit getLocalKexInit() {
233: return serverKexInit;
234: }
235:
236: /**
237: *
238: *
239: * @return
240: *
241: * @throws AlgorithmNotAgreedException
242: */
243: protected String getOutputStreamCompAlgorithm()
244: throws AlgorithmNotAgreedException {
245: return determineAlgorithm(clientKexInit.getSupportedSCComp(),
246: serverKexInit.getSupportedSCComp());
247: }
248:
249: /**
250: *
251: *
252: * @return
253: *
254: * @throws AlgorithmNotAgreedException
255: */
256: protected String getOutputStreamMacAlgorithm()
257: throws AlgorithmNotAgreedException {
258: return determineAlgorithm(clientKexInit.getSupportedSCMac(),
259: serverKexInit.getSupportedSCMac());
260: }
261:
262: /**
263: *
264: *
265: * @param ident
266: */
267: protected void setRemoteIdent(String ident) {
268: clientIdent = ident;
269: }
270:
271: /**
272: *
273: *
274: * @return
275: */
276: public String getRemoteId() {
277: return clientIdent;
278: }
279:
280: /**
281: *
282: *
283: * @param msg
284: */
285: protected void setRemoteKexInit(SshMsgKexInit msg) {
286: log.debug(msg.toString());
287: clientKexInit = msg;
288: }
289:
290: /**
291: *
292: *
293: * @return
294: */
295: protected SshMsgKexInit getRemoteKexInit() {
296: return clientKexInit;
297: }
298:
299: /**
300: *
301: *
302: * @return
303: *
304: * @throws IOException
305: * @throws TransportProtocolException
306: */
307: protected SshMsgKexInit createLocalKexInit() throws IOException {
308: SshMsgKexInit msg = new SshMsgKexInit(properties);
309: Map keys = config.getServerHostKeys();
310:
311: if (keys.size() > 0) {
312: Iterator it = keys.entrySet().iterator();
313: List available = new ArrayList();
314:
315: while (it.hasNext()) {
316: Map.Entry entry = (Map.Entry) it.next();
317:
318: if (SshKeyPairFactory.supportsKey(entry.getKey()
319: .toString())) {
320: available.add(entry.getKey());
321: } else {
322: log.warn("Server host key algorithm '"
323: + entry.getKey().toString()
324: + "' not supported");
325: }
326: }
327:
328: if (available.size() > 0) {
329: msg.setSupportedPK(available);
330: } else {
331: throw new TransportProtocolException(
332: "No server host keys available");
333: }
334: } else {
335: throw new TransportProtocolException(
336: "There are no server host keys available");
337: }
338:
339: return msg;
340: }
341:
342: /**
343: *
344: *
345: * @throws IOException
346: */
347: protected void onStartTransportProtocol() throws IOException {
348: }
349:
350: /**
351: *
352: *
353: * @param kex
354: *
355: * @throws IOException
356: * @throws KeyExchangeException
357: */
358: protected void performKeyExchange(SshKeyExchange kex)
359: throws IOException {
360: // Determine the public key algorithm and obtain an instance
361: String keyType = determineAlgorithm(clientKexInit
362: .getSupportedPublicKeys(), serverKexInit
363: .getSupportedPublicKeys());
364:
365: // Create an instance of the public key from the factory
366: //SshKeyPair pair = SshKeyPairFactory.newInstance(keyType);
367: // Get the configuration and get the relevant host key
368: Map keys = config.getServerHostKeys();
369: Iterator it = keys.entrySet().iterator();
370: SshPrivateKey pk; //privateKeyFile = null;
371:
372: while (it.hasNext()) {
373: Map.Entry entry = (Map.Entry) it.next();
374:
375: if (entry.getKey().equals(keyType)) {
376: pk = (SshPrivateKey) entry.getValue();
377: kex.performServerExchange(clientIdent, serverIdent,
378: clientKexInit.toByteArray(), serverKexInit
379: .toByteArray(), pk);
380:
381: return;
382: }
383: }
384:
385: throw new KeyExchangeException(
386: "No host key available for the determined public key algorithm");
387: }
388:
389: /**
390: *
391: *
392: * @param msg
393: *
394: * @throws IOException
395: */
396: protected void onMessageReceived(SshMessage msg) throws IOException {
397: switch (msg.getMessageId()) {
398: case SshMsgServiceRequest.SSH_MSG_SERVICE_REQUEST: {
399: onMsgServiceRequest((SshMsgServiceRequest) msg);
400:
401: break;
402: }
403: }
404: }
405:
406: /**
407: *
408: *
409: * @param encryptCSKey
410: * @param encryptCSIV
411: * @param encryptSCKey
412: * @param encryptSCIV
413: * @param macCSKey
414: * @param macSCKey
415: *
416: * @throws AlgorithmNotAgreedException
417: * @throws AlgorithmOperationException
418: * @throws AlgorithmNotSupportedException
419: * @throws AlgorithmInitializationException
420: */
421: protected void setupNewKeys(byte[] encryptCSKey,
422: byte[] encryptCSIV, byte[] encryptSCKey,
423: byte[] encryptSCIV, byte[] macCSKey, byte[] macSCKey)
424: throws AlgorithmNotAgreedException,
425: AlgorithmOperationException,
426: AlgorithmNotSupportedException,
427: AlgorithmInitializationException {
428: // Setup the encryption cipher
429: SshCipher sshCipher = SshCipherFactory
430: .newInstance(getEncryptionAlgorithm());
431: sshCipher.init(SshCipher.ENCRYPT_MODE, encryptSCIV,
432: encryptSCKey);
433: algorithmsOut.setCipher(sshCipher);
434:
435: // Setup the decryption cipher
436: sshCipher = SshCipherFactory
437: .newInstance(getDecryptionAlgorithm());
438: sshCipher.init(SshCipher.DECRYPT_MODE, encryptCSIV,
439: encryptCSKey);
440: algorithmsIn.setCipher(sshCipher);
441:
442: // Create and put our macs into operation
443: SshHmac hmac = SshHmacFactory
444: .newInstance(getOutputStreamMacAlgorithm());
445: hmac.init(macSCKey);
446: algorithmsOut.setHmac(hmac);
447: hmac = SshHmacFactory.newInstance(getInputStreamMacAlgorithm());
448: hmac.init(macCSKey);
449: algorithmsIn.setHmac(hmac);
450: }
451:
452: private void onMsgServiceRequest(SshMsgServiceRequest msg)
453: throws IOException {
454: if (acceptServices.containsKey(msg.getServiceName())) {
455: Service service = (Service) acceptServices.get(msg
456: .getServiceName());
457: service.init(Service.ACCEPTING_SERVICE, this );
458: service.start();
459: } else {
460: this .sendDisconnect(SshMsgDisconnect.SERVICE_NOT_AVAILABLE,
461: msg.getServiceName() + " is not available");
462: }
463: }
464: }
|