Sample: IBM JGSS non-JAAS server program

For more information about using the sample server program, see Downloading and running the IBM® JGSS samples.

Note: By using the code examples, you agree to the terms of the Code license and disclaimer information.
// 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;
        }
    }
}
Related concepts
Example: IBM i5/OS PASE native method for Java
Related tasks
Example: Run the Java Performance Data Converter
Related reference
Example: Internationalization of dates using the java.util.DateFormat class
Example: Internationalization of numeric display using the java.util.NumberFormat class
Example: Internationalization of locale-specific data using the java.util.ResourceBundle class
Example: Access property
Example: BLOB
Example: CallableStatement interface for IBM Developer Kit for Java
Example: Remove values from a table through another statement's cursor
Example: CLOB
Example: Create a UDBDataSource and bind it with JNDI
Example: Create a UDBDataSource, and obtain a user ID and password
Example: Create a UDBDataSourceBind and set DataSource properties
Example: DatabaseMetaData interface for IBM Developer Kit for Java - Return a list of tables
Example: Datalink
Example: Distinct types
Example: Embed SQL Statements in your Java application
Example: End a transaction
Example: Invalid user ID and password
Example: JDBC
Example: Multiple connections that work on a transaction
Example: Obtain an initial context before binding UDBDataSource
Example: ParameterMetaData
Example: Change values with a statement through another statement's cursor
Example: ResultSet interface for IBM Developer Kit for Java
Example: ResultSet sensitivity
Example: Sensitive and insensitive ResultSets
Example: Set up connection pooling with UDBDataSource and UDBConnectionPoolDataSource
Example: SQLException
Example: Suspend and resume a transaction
Example: Suspended ResultSets
Example: Test the performance of connection pooling
Example: Test the performance of two DataSources
Example: Update BLOBs
Example: Update CLOBs
Example: Use a connection with multiple transactions
Example: Use BLOBs
Example: Use CLOBs
Example: Use JTA to handle a transaction
Example: Use metadata ResultSets that have more than one column
Example: Use native JDBC and IBM Toolbox for Java JDBC concurrently
Example: Use PreparedStatement to obtain a ResultSet
Create and populate a DB2CachedRowSet
Example: Use the Statement object's executeUpdate method
Examples: JAAS HelloWorld
Example: JAAS SampleThreadSubjectLogin
Sample: IBM JGSS non-JAAS client program
Sample: IBM JGSS JAAS-enabled client program
Sample: IBM JGSS JAAS-enabled server program
Examples: IBM Java Secure Sockets Extension
Example: Call a CL program with java.lang.Runtime.exec()
Example: Call a CL command with java.lang.Runtime.exec()
Example: Call another Java program with java.lang.Runtime.exec()
Example: Call Java from C
Example: Call Java from RPG
Example: Use input and output streams for interprocess communication
Example: Java Invocation API
Examples: Use the Java Native Interface for native methods
Example: Use sockets for interprocess communication
Examples: Change your Java code to use client socket factories
Examples: Change your Java code to use server socket factories
Examples: Change your Java client to use secure sockets layer
Examples: Change your Java server to use secure sockets layer