Source code: com/ciphercore/Server.java
1 /* $Id: Server.java,v 1.12 2001/04/02 08:42:50 cvsbob Exp $ */
2
3 /*
4 * Server.java, child spawning ciphercore master server.
5 * Copyright (C) 2001 Robert Bushman.
6 *
7 * I reserve the right to release this program under seperate license.
8 * If you require a special license grant contact Robert Bushman.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23 * 02111-1307, USA.
24 */
25
26 package com.ciphercore;
27
28 import java.io.BufferedInputStream;
29 import java.io.DataInputStream;
30 import java.io.DataOutputStream;
31 import java.io.File;
32 import java.io.FileInputStream;
33 import java.io.FileNotFoundException;
34 import java.io.InputStream;
35 import java.io.IOException;
36 import java.io.OutputStream;
37 import java.math.BigInteger;
38 import java.net.URL;
39 import java.net.Socket;
40 import java.net.ServerSocket;
41 import java.security.AlgorithmParameters;
42 import java.security.AlgorithmParameterGenerator;
43 import java.security.InvalidAlgorithmParameterException;
44 import java.security.NoSuchAlgorithmException;
45 import java.security.Provider;
46 import java.security.Security;
47 import java.security.spec.InvalidKeySpecException;
48 import java.security.spec.InvalidParameterSpecException;
49 import java.util.HashSet;
50 import java.util.HashMap;
51 import java.util.Properties;
52 import java.util.StringTokenizer;
53 import javax.crypto.spec.DHParameterSpec;
54 import javax.crypto.interfaces.DHPublicKey;
55 import javax.crypto.interfaces.DHPrivateKey;
56
57 import com.traxel.crypto.CipherPartner;
58 import com.traxel.crypto.CryptoUtil;
59 import com.traxel.crypto.DHUtil;
60 import com.traxel.util.ClassUtil;
61
62 /**
63 * The CipherCore server. This is the class that responds to
64 * client requests. Each request specifies a child type, which
65 * the Server checks against it's list of allowed types. If
66 * the type is allowed, it spawns the child, gives it a copy
67 * of the servers Diffie Hellman key, and returns to waiting
68 * for the next child.
69 * <br><br>
70 * <b>Basic Usage</b><pre><tt>
71 * java com.ciphercore.Server --children=pkg1.Class1:pkg2.Class2
72 * </pre></tt>
73 */
74 public class Server {
75
76 // ----------------------------------------------------------
77 // CONSTANTS
78 // ----------------------------------------------------------
79
80 /** If no port is specified, run on 5943. */
81 public static final int DEFAULT_PORT = 5943;
82 public static final String DEFAULT_PROVIDER_NAME_OLD1 =
83 "com.sun.crypto.provider.SunJCE";
84 public static final String DEFAULT_PROVIDER_NAME_OLD2 =
85 "cryptix.jce.provider.CryptixCrypto";
86 public static final String DEFAULT_PROVIDER_NAME =
87 "org.bouncycastle.jce.provider.BouncyCastleProvider";
88 // ---------------------------------------------------------
89 // CONSTRUCTORS
90 // ---------------------------------------------------------
91
92 /**
93 * Create a Server which allows the specified children, on the
94 * default port (5943), in the specified mode
95 * (Server.SECURE_MODE or Server.INSECURE_MODE).
96 */
97 public Server( HashSet approvedChildren ) {
98 this( approvedChildren, DEFAULT_PORT );
99 }
100
101 /**
102 * Create a Server which allows the specified children, on the
103 * specified port, in the specified mode
104 * (Server.SECURE_MODE or Server.INSECURE_MODE).
105 */
106 public Server( HashSet approvedChildren, int port ) {
107 setApprovedChildren( approvedChildren );
108 setPort( port );
109 CryptoUtil.initializeProvider( getProviderName() );
110 initializeSelf();
111 }
112
113 private void initializeSelf() {
114 locateFiles();
115 readBasicCfg();
116 readAdvancedCfg();
117 readUserHashes();
118 readKeys();
119 }
120
121 // ------------------------------------------------------------
122 // PUBLIC API
123 // ------------------------------------------------------------
124
125 /**
126 * This method accepts the incoming client and hands the socket
127 * off to handleClient( socket ). In the future, this is where
128 * service independant IP based blocking (IE: across the board
129 * denial) should occur.
130 */
131 public void run() {
132 try {
133 ServerSocket serverSocket = new ServerSocket( getPort() );
134 while( true ) {
135 System.out.println( "Server listening on " + getPort() );
136 Socket socket = serverSocket.accept();
137 handleClient( socket );
138 }
139 } catch( IOException e ) {
140 e.printStackTrace();
141 System.exit( 1 );
142 }
143 }
144
145 // -----------------------------------------------------------
146 // INTERNAL API
147 // ----------------------------------------------------------
148
149 /**
150 * This method determines the requested client type, and spawns
151 * the appropriate child. In the future, this is where
152 * service dependant IP based blocking (IE: only specific
153 * accounts can spawn childs of type X) should occur.
154 */
155 protected void handleClient( Socket socket ) throws IOException {
156 try {
157 InputStream rawIn = socket.getInputStream();
158 OutputStream rawOut = socket.getOutputStream();
159 BufferedInputStream buffIn = new BufferedInputStream( rawIn );
160 DataInputStream in = new DataInputStream( buffIn );
161 DataOutputStream out = new DataOutputStream( rawOut );
162
163 String childClassName = readNextString( in );
164 System.out.println( "Request For Child: " + childClassName );
165
166 launchChild( childClassName, socket );
167
168 System.out.println( "Child Launched." );
169
170 } catch( ClassNotFoundException e ) {
171 e.printStackTrace();
172 } catch( InstantiationException e ) {
173 e.printStackTrace();
174 } catch( IllegalAccessException e ) {
175 e.printStackTrace();
176 }
177 }
178
179 /**
180 * A convenience method that reads the next string.
181 */
182 protected static String readNextString( DataInputStream in )
183 throws IOException {
184
185 int length;
186 byte[] bytes;
187 String string;
188
189 length = in.readInt();
190 bytes = new byte[ length ];
191 in.readFully( bytes );
192 string = new String( bytes );
193
194 return( string );
195 }
196
197 /**
198 * Verifies that the specified class is allowed to run in this server,
199 * then instantiates the child, gives it a reference to this child
200 * type's interprocess object, gives it a copy of the DHPartner,
201 * and starts the child.
202 *
203 * @param childClassName The name of the requested child class.
204 * @exception ClassNotFoundException
205 * @exception InstantiationException
206 * @exception IllegalAccessException
207 */
208 protected Child launchChild( String childClassName, Socket socket )
209 throws ClassNotFoundException,
210 InstantiationException,
211 IllegalAccessException {
212 if( ! isApproved( childClassName ) ) {
213 throw new IllegalAccessException
214 ( childClassName + " is not approved on this server." );
215 }
216 Class childClass = Class.forName( childClassName );
217 Child child = (Child)childClass.newInstance();
218
219 // Hand off the current IPO
220 child.initializeInterProcessObject( getIPO( childClassName ) );
221 // In case the child new'ed the IPO, update our copy.
222 putIPO( childClassName, child.getInterProcessObject() );
223 // Give the child a duplicate of the core DHPartner
224 child.setCipherSelf
225 ( new CipherPartner( childClassName, getCipherSelf() ) );
226 // Give the child it's socket.
227 child.setSocket( socket );
228 // Let 'er rip.
229 child.start();
230
231 return( child );
232 }
233
234 protected void locateFiles() {
235 String byteCodePath = ClassUtil.pathToBytecode( this );
236 String configPath = null;
237 String jarLocation = "/lib/";
238 String classLocation = "/src/com/ciphercore/Server.class";
239 if( byteCodePath.indexOf( ".jar" ) > -1 ) {
240 configPath = byteCodePath.substring
241 ( 5, byteCodePath.lastIndexOf( jarLocation ) );
242 } else {
243 configPath = byteCodePath.substring
244 ( 5, byteCodePath.lastIndexOf( classLocation ) );
245 }
246 configPath = configPath + "/config/server";
247
248 setBasicCfgLocation( configPath + "/basic.cfg" );
249 setAdvancedCfgLocation( configPath + "/advanced.cfg" );
250 setPubKeyLocation( configPath + "/public.key" );
251 setPubSha1Location( configPath + "/public.sha1" );
252 setPrivKeyLocation( configPath + "/private.key" );
253 setUserHashesLocation( configPath + "/user.hashes" );
254 }
255
256 protected void readBasicCfg() {
257 FileInputStream fileIn;
258 Properties properties;
259
260 properties = new Properties();
261 try {
262 fileIn = new FileInputStream( getBasicCfgLocation() );
263 properties.load( fileIn );
264 } catch( Exception e ) {
265 e.printStackTrace();
266 System.err.println( "Failed to access basic config file." );
267 System.err.println( " Location: " + getBasicCfgLocation() );
268 System.exit( 1 );
269 }
270 }
271
272 protected void readAdvancedCfg() {
273 FileInputStream fileIn;
274 Properties properties;
275
276 properties = new Properties();
277 try {
278 fileIn = new FileInputStream( getAdvancedCfgLocation() );
279 properties.load( fileIn );
280 } catch( Exception e ) {
281 e.printStackTrace();
282 System.err.println( "Failed to access advanced config file." );
283 System.err.println( " Location: " + getAdvancedCfgLocation() );
284 System.exit( 1 );
285 }
286 }
287
288 protected void readUserHashes() {
289 FileInputStream fileIn;
290 Properties userHashes;
291
292 userHashes = new Properties();
293 try {
294 fileIn = new FileInputStream( getUserHashesLocation() );
295 userHashes.load( fileIn );
296 } catch( Exception e ) {
297 e.printStackTrace();
298 System.err.println( "Failed to access user hashes file." );
299 System.err.println( " Location: " + getUserHashesLocation() );
300 System.exit( 1 );
301 }
302 setUserHashes( userHashes );
303 }
304
305 protected void readKeys() {
306 FileInputStream pubIn;
307 FileInputStream privIn;
308 File sha1File;
309 DHPublicKey pubKey;
310 DHPrivateKey privKey;
311 CipherPartner self;
312 String keyLocations =
313 " " + getPubKeyLocation() + "\n"
314 + " " + getPrivKeyLocation() + "\n"
315 + " " + getPubSha1Location();
316
317 try {
318 self = new CipherPartner( "SERVER",
319 getPubKeyLocation(),
320 getPrivKeyLocation() );
321 setCipherSelf( self );
322 } catch( FileNotFoundException e ) {
323 System.out.println();
324 e.printStackTrace();
325 System.out.println
326 ( "WARNING: One or more of the key files not found." );
327 System.out.println( keyLocations );
328 generateKeys();
329 System.exit( 1 );
330 } catch( InvalidKeySpecException e ) {
331 System.out.println();
332 e.printStackTrace();
333 System.out.println( "One or more of the key files is corrupt." );
334 System.out.println( keyLocations );
335 System.out.println( "Deleting the files will force regeneration." );
336 System.exit( 1 );
337 } catch( IOException e ) {
338 System.out.println();
339 e.printStackTrace();
340 System.out.println
341 ( "WARNING: One or more of the key files could not be read." );
342 System.out.println( keyLocations );
343 System.exit( 1 );
344 } catch( Exception e ) {
345 System.out.println();
346 e.printStackTrace();
347 System.out.println
348 ( "WARNING: Diffie Hellman key file error." );
349 System.out.println( keyLocations );
350 System.exit( 1 );
351 }
352 }
353
354 protected void generateKeys() {
355 CipherPartner self;
356
357 System.out.println( "Beginning Diffie Hellman key generation." );
358
359 try {
360 self = new CipherPartner( "SERVER", getParams() );
361 self.exportDhKeys( getPubKeyLocation(),
362 getPrivKeyLocation(),
363 getPubSha1Location() );
364 } catch( Exception e ) {
365 System.out.println();
366 System.out.println( "Failed to write Diffie Hellman key files." );
367 System.out.println( " " + getPubKeyLocation() + "\n"
368 + " " + getPrivKeyLocation() + "\n"
369 + " " + getPubSha1Location() );
370 System.out.println( "ALERT: Make sure the config directory "
371 + "exists and is writeable." );
372 System.exit( 1 );
373 }
374
375 System.out.println();
376 System.out.println( "You can now secure the file "
377 + getPrivKeyLocation() + " if you wish." );
378 System.out.println( "NOTE: It must be readable by this process." );
379 System.out.println( "When you're ready, restart the server." );
380 System.exit( 1 );
381 }
382
383 // --------------------------------------------------------
384 // INSTANCE PARAMETERS AND ACCESSORS
385 // --------------------------------------------------------
386
387 // PARAMETERS
388 private int _port = DEFAULT_PORT;
389 private DHParameterSpec _params = DHStock.PARAMS;
390 private CipherPartner _cipherSelf;
391 private HashMap _interProcessObjects = new HashMap();
392 private HashSet _approvedChildren;
393 private String _basicCfgLocation;
394 private String _advancedCfgLocation;
395 private String _pubKeyLocation;
396 private String _pubSha1Location;
397 private String _privKeyLocation;
398 private String _userHashesLocation;
399 private Properties _userHashes;
400
401 // SETTERS
402 protected void setPort( int port ) { _port = port; }
403 protected void setParams( DHParameterSpec params ) { _params = params; }
404 protected void setCipherSelf( CipherPartner self ) { _cipherSelf = self; }
405 protected void putIPO( String childClassName, InterProcessObject ipo ) {
406 _interProcessObjects.put( childClassName, ipo );
407 }
408 protected void setBasicCfgLocation( String location ) {
409 _basicCfgLocation = location;
410 }
411 protected void setAdvancedCfgLocation( String location ) {
412 _advancedCfgLocation = location;
413 }
414 protected void setPubKeyLocation( String location ) {
415 _pubKeyLocation = location;
416 }
417 protected void setPubSha1Location( String location ) {
418 _pubSha1Location = location;
419 }
420 protected void setPrivKeyLocation( String location ) {
421 _privKeyLocation = location;
422 }
423 protected void setUserHashesLocation( String location ) {
424 _userHashesLocation = location;
425 }
426 protected void setUserHashes( Properties userHashes ) {
427 _userHashes = userHashes;
428 }
429
430 protected void setApprovedChildren( HashSet approvedChildren ) {
431 _approvedChildren = approvedChildren;
432 }
433
434 // GETTERS
435 public int getPort() { return( _port ); }
436 public DHParameterSpec getParams() { return( _params ); }
437 public CipherPartner getCipherSelf() { return( _cipherSelf ); }
438 public boolean isApproved( String childClassName ) {
439 return( _approvedChildren.contains( childClassName ) );
440 }
441 public String getProviderName() { return( DEFAULT_PROVIDER_NAME ); }
442 public String getBasicCfgLocation() { return( _basicCfgLocation ); }
443 public String getAdvancedCfgLocation() { return( _advancedCfgLocation ); }
444 public String getPubKeyLocation() { return( _pubKeyLocation ); }
445 public String getPubSha1Location() { return( _pubSha1Location ); }
446 public String getPrivKeyLocation() { return( _privKeyLocation ); }
447 public String getUserHashesLocation() { return( _userHashesLocation ); }
448 public Properties getUserHashes() { return( _userHashes ); }
449
450 public InterProcessObject getIPO( String childClassName ) {
451 return( (InterProcessObject)_interProcessObjects.get(childClassName) );
452 }
453
454 // CONVENIENCE METHODS
455 public String getUserHash( String userName ) {
456 return( (String)getUserHashes().get( userName ) );
457 }
458
459 // ------------------------------------------------------
460 // RUNTIME
461 // ------------------------------------------------------
462
463 protected static HashSet parseApprovedChildren( String[] args ) {
464 boolean childrenSpecified = false;
465 HashSet approvedChildren = new HashSet();
466 if( args != null ) {
467 for( int i = 0; i < args.length; i++ ) {
468 if( args[i].indexOf( "--children=" ) == 0 ) {
469 String children = args[i].substring( 11 );
470 StringTokenizer toker = new StringTokenizer( children, ":" );
471 while( toker.hasMoreTokens() ) {
472 approvedChildren.add( toker.nextToken() );
473 }
474 childrenSpecified = true;
475 }
476 }
477 }
478 if( ! childrenSpecified ) {
479 approvedChildren = new HashSet();
480 approvedChildren.add( "com.ciphercore.samples.ChildDemo" );
481 }
482 return( approvedChildren );
483 }
484
485 public static void main( String[] args ) throws IOException {
486 Server server;
487 HashSet approvedChildren = parseApprovedChildren( args );
488
489 server = new Server( approvedChildren );
490
491 server.run();
492 }
493 }
494