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;
}
}
}