For more information about using the sample server program, see Downloading and running the IBM® JGSS samples.
// IBM JGSS 1.0 Sample Server Program package com.ibm.security.jgss.test; import org.ietf.jgss.*; import com.ibm.security.jgss.Debug; import java.io.*; import java.net.*; import java.util.*; /** * A JGSS sample server; to be used in conjunction with a JGSS sample client. * * It continuously listens for client connections, * spawning a thread to service an incoming connection. * It is capable of running multiple threads concurrently. * In other words, it can service multiple clients concurrently. * * Each thread first establishes a context with the client * and then waits for a wrapped message followed by a MIC. * It assumes that the client calculated the MIC over the plain * text wrapped by the client. * * If the client delegates its credential to the server, the delegated * credential is used to communicate with a secondary server. * * Also, the server can be started to act as a client as well as * a server (using the -b option). In this case, the first * thread spawned by the server uses the server principal's own credential * to communicate with the secondary server. * * The secondary server must have been started prior to the (primary) server * initiating contact with it (the scondary server). * In communicating with the secondary server, the primary server acts as * a JGSS initiator (i.e., client), establishing a context and engaging in * wrap and MIC per-message exchanges with the secondary server. * * The server takes input parameters, and complements it * with information from the jgss.ini file; any required input not * supplied on the command line is taking from the jgss.ini file. * Built-in defaults are used if there is no jgss.ini file or if a particular * variable is not specified in the ini file. * * Usage: Server [options] * * The -? option produces a help message including supported options. * * This sample server does not use JAAS. * It sets the JAVA variable * javax.security.auth.useSubjectCredsOnly to false * so that JGSS will not acquire credentials through JAAS. * The server can be run against the JAAS sample clients and servers. * See {@link JAASServer JAASServer} for a sample server that uses JAAS. */ class Server implements Runnable { /* * NOTES: * This class, Server, is expected to be run in concurrent * multiple threads. The static variables consist of variables * set from command-line arguments and variables (such as * the server's own credentials, gssCred) that are set once during * during initialization. These variables do not change * once set and are shared between all running threads. * * The only static variable that is changed after being set initially * is the variable 'beenInitiator' which is set 'true' * by the first thread to run the server as initiator using * the server's own creds. This ensures the server is run as an initiator * once only. Querying and modifying 'beenInitiator' is synchronized * between the threads. * * The variable 'tcp' is non-static and is set per thread * to represent the socket on which the client being serviced * by the thread connected. */ private static Util testUtil = null; private static int myPort = 0; private static Debug debug = new Debug(); private static String myName = null; private static GSSCredential gssCred = null; private static String serviceNameNoRealm = null; private static String serviceHost = null; private static int servicePort = 0; private static String serviceMsg = null; private static GSSManager mgr = null; private static GSSName gssName = null; private static String program = "Server"; private static boolean clientServer = false; private static boolean primaryServer = true; private static boolean beenInitiator = false; private static final String usageString = "\t[-?] [-# number] [-d | -n name] [-p port]" + "\n\t[-s serverName] [-h serverHost [:port]] [-P serverPort] [- msg]" + "\n" + "\n -?\t\t\thelp; produces this message" + "\n -# number\t\tWhether primary or secondary server" + " \n\t\t\t(1 = primary, 2 = secondary; default = first)" + "\n -n name\t\tthe server's principal name (without realm)" + "\n -p port\t\tthe port on which the server will be listening" + "\n -s serverName\t\tsecondary server's principal name" + " (without realm)" + "\n -h serverHost[:port]\tsecondary server's hostname" + " (and optional port number)" + "\n -P port\t\tsecondary server's port number" + "\n -m msg\t\tmessage to send to secondary server" + "\n -b \t\trun as both client and server" + " using the server's owns credentials"; // Non-static variables are thread-specific // since each thread runs a separate instance of this class. private String debugPrefix = null; private TCPComms tcp = null; static { try { testUtil = new Util(); } catch (Exception exc) { exc.printStackTrace(); System.exit(1); } } Server (Socket socket) throws Exception { debugPrefix = program + ": "; tcp = new TCPComms(socket); } Server (String program) throws Exception { debugPrefix = program + ": "; this.program = program; } Server (String program, boolean useSubjectCredsOnly) throws Exception { this(program); setUseSubjectCredsOnly(useSubjectCredsOnly); } void setUseSubjectCredsOnly(boolean useSubjectCredsOnly) { final String subjectOnly = useSubjectCredsOnly ? "true" : "false"; final String property = "javax.security.auth.useSubjectCredsOnly"; String temp = (String)java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction(property)); if (temp == null) { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "setting useSubjectCredsOnly property to " + (useSubjectCredsOnly ? "true" : "false")); // Property not set. Set it to the specified value. java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run() { System.setProperty(property, subjectOnly); return null; } }); } else { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "useSubjectCredsOnly property already set " + "in JVM to " + temp); } } private void init(boolean primary, String myNameWithoutRealm, int port, String serverNameWithoutRealm, String serverHostname, int serverPort, String message, boolean clientServer) throws Exception { primaryServer = primary; this.clientServer = clientServer; myName = myNameWithoutRealm; // my port if (port > 0) { myPort = port; } else if (primary) { myPort = testUtil.getDefaultServicePort(); } else { myPort = testUtil.getDefaultService2Port(); } if (primary) { ///// peer's name if (serverNameWithoutRealm != null) { serviceNameNoRealm = serverNameWithoutRealm; } else { serviceNameNoRealm = testUtil.getDefaultService2PrincipalWithoutRealm(); } // peer's host if (serverHostname != null) { if (serverHostname.equalsIgnoreCase("localHost")) { serverHostname = InetAddress.getLocalHost().getHostName(); } serviceHost = serverHostname; } else { serviceHost = testUtil.getDefaultService2Hostname(); } // peer's port if (serverPort > 0) { servicePort = serverPort; } else { servicePort = testUtil.getDefaultService2Port(); } // message for peer if (message != null) { serviceMsg = message; } else { serviceMsg = "Hi there! I am a server." + "But I can be a client, too"; } } String temp = debugPrefix + "details" + "\n\tPrimary:\t" + primary + "\n\tName:\t\t" + myName + "\n\tPort:\t\t" + myPort + "\n\tClient+server:\t" + clientServer; if (primary) { temp += "\n\tOther Server:" + "\n\t\tName:\t" + serviceNameNoRealm + "\n\t\tHost:\t" + serviceHost + "\n\t\tPort:\t" + servicePort + "\n\t\tMsg:\t" + serviceMsg; } debug.out(Debug.OPTS_CAT_APPLICATION, temp); } void initialize() throws GSSException { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "creating GSSManager"); mgr = GSSManager.getInstance(); int usage = clientServer ? GSSCredential.INITIATE_AND_ACCEPT : GSSCredential.ACCEPT_ONLY; if (myName != null) { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "creating GSSName for " + myName); gssName = mgr.createName(myName, GSSName.NT_HOSTBASED_SERVICE); Oid krb5MechanismOid = new Oid("1.2.840.113554.1.2.2"); gssName.canonicalize(krb5MechanismOid); debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "Canonicalized GSSName=" + gssName); } else gssName = null; debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "creating" + ((gssName == null)? " default " : " ") + "credential"); gssCred = mgr.createCredential( gssName, GSSCredential.DEFAULT_LIFETIME, (Oid)null, usage); if (gssName == null) { gssName = gssCred.getName(); myName = gssName.toString(); debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "default credential principal=" + myName); } } void processArgs(String[] args) throws Exception { String port = null; String name = null; int iport = 0; String sport = null; int isport = 0; String sname = null; String shost = null; String smessage = null; boolean primary = true; String status = null; boolean defaultPrinc = false; boolean clientServer = false; GetOptions options = new GetOptions(args, "?#:p:n:P:s:h:m:b"); int ch = -1; while ((ch = options.getopt()) != options.optEOF) { switch(ch) { case '?': printUsage(); System.exit(1); case '#': if (status == null) status = options.optArgGet(); continue; case 'p': if (port == null) port = options.optArgGet(); continue; case 'n': if (name == null) name = options.optArgGet(); continue; case 'b': clientServer = true; continue; ////// The other server case 'P': if (sport == null) sport = options.optArgGet(); continue; case 'm': if (smessage == null) smessage = options.optArgGet(); continue; case 's': if (sname == null) sname = options.optArgGet(); continue; case 'h': if (shost == null) { shost = options.optArgGet(); int p = shost.indexOf(':'); if (p != -1) { String temp1 = shost.substring(0, p); if (sport == null) sport = shost.substring (p+1, shost.length()).trim(); shost = temp1; } } continue; } } if (defaultPrinc && (name != null)) { System.out.println( "ERROR: '-d' and '-n ' options are mutually exclusive"); printUsage(); System.exit(1); } if (status != null) { int p = -1; try { p = Integer.parseInt(status); } catch (Exception exc) { System.out.println( "Bad status input: "+status); } if (p != -1) { primary = (p == 1); } } if (port != null) { int p = -1; try { p = Integer.parseInt(port); } catch (Exception exc) { System.out.println( "Bad port input: "+port); } if (p != -1) iport = p; } if (sport != null) { int p = -1; try { p = Integer.parseInt(sport); } catch (Exception exc) { System.out.println( "Bad server port input: "+port); } if (p != -1) isport = p; } init(primary, // first or second server name, // my name iport, // my port sname, // other server's name shost, // other server's hostname isport, // other server's port smessage, // msg for other server clientServer); // whether to run as initiator with own creds } void processRequests() throws Exception { ServerSocket ssocket = null; Server server = null; try { ssocket = new ServerSocket(myPort); do { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "listening on port " + myPort + " ..."); Socket csocket = ssocket.accept(); debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "incoming connection on " + csocket); server = new Server(csocket); // set client socket per thread Thread thread = new Thread(server); thread.start(); if (!thread.isAlive()) server.dispose(); // close the client socket } while(true); } catch (Exception exc) { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "*** ERROR processing requests ***"); exc.printStackTrace(); } finally { try { if (ssocket != null) ssocket.close(); // close the server socket if (server != null) server.dispose(); // close the client socket } catch (Exception exc) {} } } void dispose() { try { if (tcp != null) { tcp.close(); tcp = null; } } catch (Exception exc) {} } boolean establishContext(GSSContext context) throws Exception { byte[] response = null; byte[] request = null; debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "establishing context"); do { request = tcp.receive(); if (request == null || request.length == 0) { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "Received no data; perhaps client disconnected"); return false; } debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "accepting"); if ((response = context.acceptSecContext (request, 0, request.length)) != null) { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "sending response"); tcp.send(response); } } while(!context.isEstablished()); debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "context established - " + context); return true; } byte[] unwrap(GSSContext context, byte[] msg) throws Exception { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "unwrapping"); MessageProp mp = new MessageProp(true); byte[] unwrappedMsg = context.unwrap(msg, 0, msg.length, mp); debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "unwrapped msg is:"); debug.out(Debug.OPTS_CAT_APPLICATION, unwrappedMsg); return unwrappedMsg; } void verifyMIC (GSSContext context, byte[] mic, byte[] raw) throws Exception { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "verifying MIC"); MessageProp mp = new MessageProp(true); context.verifyMIC(mic, 0, mic.length, raw, 0, raw.length, mp); debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "successfully verified MIC"); } void useDelegatedCred(GSSContext context) throws Exception { GSSCredential delCred = context.getDelegCred(); if (delCred != null) { if (primaryServer) { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "Primary server received delegated cred; using it"); runAsInitiator(delCred); // using delegated creds } else { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "Non-primary server received delegated cred; " + "ignoring it"); } } else { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "ERROR: null delegated cred"); } } public void run() { byte[] response = null; byte[] request = null; boolean unwrapped = false; GSSContext context = null; try { Thread currentThread = Thread.currentThread(); String threadName = currentThread.getName(); debugPrefix = program + " " + threadName + ": "; debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "servicing client ..."); debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "creating GSSContext"); context = mgr.createContext(gssCred); // First establish context with the initiator. if (!establishContext(context)) return; // Then process messages from the initiator. // We expect to receive a wrapped message followed by a MIC. // The MIC should have been calculated over the plain // text that we received wrapped. // Use delegated creds if any. // Then run as initiator using own creds if necessary; only // the first thread does this. do { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "receiving per-message request"); request = tcp.receive(); if (request == null || request.length == 0) { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "Received no data; perhaps client disconnected"); return; } // Expect wrapped message first. if (!unwrapped) { response = unwrap(context, request); unwrapped = true; continue; // get next request } // Followed by a MIC. verifyMIC(context, request, response); // Impersonate the initiator if it delegated its creds to us. if (context.getCredDelegState()) useDelegatedCred(context); debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "clientServer=" + clientServer + ", beenInitiator=" + beenInitiator); // If necessary, run as initiator using our own creds. if (clientServer) runAsInitiatorOnce(currentThread); debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "done"); return; } while(true); } catch (Exception exc) { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "ERROR"); exc.printStackTrace(); // Squelch per-thread exceptions so we don't bring // the server down because of exceptions in // individual threads. return; } finally { if (context != null) { try { context.dispose(); } catch (Exception exc) {} } } } synchronized void runAsInitiatorOnce(Thread thread) throws InterruptedException { if (!beenInitiator) { // set flag true early to prevent subsequent threads // from attempting to runAsInitiator. beenInitiator = true; debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "About to run as initiator with own creds ..."); //thread.sleep(30*1000, 0); runAsInitiator(); } } void runAsInitiator(GSSCredential cred) { Client client = null; try { client = new Client(cred, serviceNameNoRealm, serviceHost, servicePort, serviceMsg); client.initialize(); BitSet flags = new BitSet(); flags.set(Util.CONTEXT_OPTS_MUTUAL); flags.set(Util.CONTEXT_OPTS_CONF); flags.set(Util.CONTEXT_OPTS_INTEG); client.interactWithAcceptor(flags); } catch (Exception exc) { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "Exception running as initiator"); exc.printStackTrace(); } finally { try { client.dispose(); } catch (Exception exc) {} } } void runAsInitiator() { if (clientServer) { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "running as initiator with own creds"); runAsInitiator(gssCred); // use own creds; } else { debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "Cannot run as initiator with own creds " + "\nbecause not running as both initiator and acceptor."); } } void printUsage() { System.out.println(program + usageString); } public static void main(String[] args) throws Exception { System.out.println(debug.toString()); // XXXXXXX String programName = "Server"; try { Server server = new Server(programName, false); // don't use creds from Subject server.processArgs(args); server.initialize(); server.processRequests(); } catch (Exception exc) { debug.out(Debug.OPTS_CAT_APPLICATION, programName + ": EXCEPTION"); exc.printStackTrace(); throw exc; } } }