*
===========================================================================
* Licensed Materials - Property of IBM
* IBM Security Software Development Kit, Java
(tm) Technology Edition
*
* (C) Copyright IBM Corp. 2002, 2005 All
Rights Reserved.
*
* US Government
Users Restricted Rights - Use, duplication or
*
disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*
===========================================================================
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.
* <p>
* 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.
* <p>
* 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.
* <p>
* If the client delegates its credential to
the server, the delegated
* credential is used to communicate with a
secondary server.
* <p>
* Also, the server can be started to act as a
client as well as
* a server (using the
<code>-b</code> option). In this case, the first
* thread spawned by the server uses the server
principal's own credential
* to communicate with the secondary server.
* <p>
* 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.
* <p>
* 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.
* <p>
* Usage: Server [options]
* <p>
* The -? option produces a help message
including supported options.
* <p>
* This sample server does not use JAAS.
* It sets the JAVA variable
*
<code>javax.security.auth.useSubjectCredsOnly</code> to
<code>false</code>
* 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.
*
* @author Thomas Owusu
*/
//
Secondary server uses ByteArray streams.
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 boolean useStreams= false;
private static final String usageString =
"\t[-?] [-# number] [-n name] [-p
port]"
+ "\n\t[-s serverName] [-h
serverHost [:port]] [-P serverPort] [-m 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;
if (!primaryServer) {
useStreams = true;
debug.out(Debug.OPTS_CAT_APPLICATION,
debugPrefix +
"Using streams");
}
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 an acceptor."
+ " But I can
be an initiator, 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);
//gssName.canonicalize(GSSManagerImpl.MECH_TYPE_KRB5);
myName = gssName.toString();
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);
}
int lifetime =
gssCred.getRemainingLifetime();
debug.out(Debug.OPTS_CAT_APPLICATION,
debugPrefix
+ "lifetime remaining
on cred: " + lifetime + " secs");
Oid[] mechs = gssCred.getMechs();
if (usage == GSSCredential.INITIATE_AND_ACCEPT)
{
lifetime =
gssCred.getRemainingInitLifetime(mechs[0]);
debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix
+ "Init lifetime
remaining on cred for mech " + mechs[0]
+ ": " + lifetime
+ " secs");
}
lifetime =
gssCred.getRemainingAcceptLifetime(mechs[0]);
debug.out(Debug.OPTS_CAT_APPLICATION,
debugPrefix
+ "Accept lifetime remaining
on cred for mech " + mechs[0]
+ ": " + lifetime +
" secs");
debug.out(Debug.OPTS_CAT_APPLICATION,
debugPrefix + gssCred);
}
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 clientServer = false;
if (args != null && args.length
> 0) {
for (int i=0; i<args.length;
i++) {
if
("-?".equals(args[i])) {
printUsage();
System.exit(1);
} else if
("-#".equals(args[i])) {
if (status == null) {
if (i+1 >=
args.length) {
noArgForOptionError("-#");
}
status = args[++i];
continue;
} else {
duplicateOptionError("-#");
}
} else if
("-p".equals(args[i])) {
if (port == null) {
if (i+1 >=
args.length) {
noArgForOptionError("-p");
}
port = args[++i];
continue;
} else {
duplicateOptionError("-p");
}
} else if ("-n".equals(args[i]))
{
if (name == null) {
if (i+1 >=
args.length) {
noArgForOptionError("-n");
}
name = args[++i];
continue;
} else {
duplicateOptionError("-n");
}
} else if
("-b".equals(args[i])) {
if (!clientServer) {
clientServer = true;
continue;
} else {
duplicateOptionError("-b");
}
} else if
("-P".equals(args[i])) { ////// The other server
if (sport == null) {
if (i+1 >= args.length) {
noArgForOptionError("-P");
}
sport = args[++i];
continue;
} else {
duplicateOptionError("-P");
}
} else if
("-m".equals(args[i])) {
if (smessage == null) {
if (i+1 >=
args.length) {
noArgForOptionError("-m");
}
smessage = args[++i];
continue;
} else {
duplicateOptionError("-m");
}
} else if
("-s".equals(args[i])) {
if (sname == null) {
if (i+1 >=
args.length) {
noArgForOptionError("-s");
}
sname = args[++i];
continue;
} else {
duplicateOptionError("-s");
}
} else if
("-h".equals(args[i])) {
if (shost == null) {
if (i+1 >=
args.length) {
noArgForOptionError("-h");
}
shost = args[++i];
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;
} else {
duplicateOptionError("-h");
}
}
}
}
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
}
private void duplicateOptionError(String
option) {
System.out.println("Duplicate
option: " + option);
printUsage();
System.exit(1);
}
private void noArgForOptionError(String
option) {
System.out.println("Missing argument
for option: " + option);
printUsage();
System.exit(1);
}
void processRequests() throws Exception
{
ServerSocket ssocket = null;
Server server = null;
try {
ssocket = new ServerSocket(myPort);
do {
debug.out(Debug.OPTS_CAT_APPLICATION,
debugPrefix + "
(" + myName + ")\n"
+ "\tlistening 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");
debug.out(Debug.OPTS_CAT_APPLICATION,
request);
if (!useStreams) {
response =
context.acceptSecContext(
request,
0, request.length);
} else {
ByteArrayOutputStream bos = new
ByteArrayOutputStream();
ByteArrayInputStream bis =
null;
bis = new
ByteArrayInputStream(request);
context.acceptSecContext(bis,
bos);
response = bos.toByteArray();
}
if (response != null) {
debug.out(Debug.OPTS_CAT_APPLICATION,
debugPrefix +
"sending response");
tcp.send(response);
}
} while(!context.isEstablished());
if (debug.on(Debug.OPTS_CAT_APPLICATION))
{
String msg = "established
context with initiator "
+
context.getSrcName();
debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + msg);
boolean delegationState =
context.getCredDelegState();
msg =
"getCredDelegState=" + delegationState;
debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + msg);
}
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 = null;
if (!useStreams) {
unwrappedMsg = context.unwrap(msg,
0, msg.length, mp);
} else {
ByteArrayOutputStream bos = new
ByteArrayOutputStream();
ByteArrayInputStream bis = new
ByteArrayInputStream(msg);
context.unwrap(bis, bos, mp);
unwrappedMsg = bos.toByteArray();
}
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);
if (!useStreams) {
context.verifyMIC(mic, 0, mic.length, raw,
0, raw.length, mp);
} else {
ByteArrayInputStream tokenBis = new
ByteArrayInputStream(mic);
ByteArrayInputStream msgBis = new
ByteArrayInputStream(raw);
context.verifyMIC(tokenBis, msgBis,
mp);
}
debug.out(Debug.OPTS_CAT_APPLICATION,
debugPrefix +
"successfully verified MIC");
}
void useDelegatedCred(GSSCredential
delCred, String msg) throws Exception
{
if (delCred != null)
{
if (primaryServer)
{
debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix +
"Primary server
received delegated cred; using it");
runAsInitiator(delCred, msg);
// 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())
{
GSSCredential delCred =
context.getDelegCred();
useDelegatedCred(delCred, new
String(response));
}
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,
String msg)
{
Client client = null;
try {
client = new Client(cred,
serviceNameNoRealm,
serviceHost,
servicePort,
msg == null ?
serviceMsg : msg,
true); // use
streams
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,
serviceMsg); // use own creds, own message;
}
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;
}
}
}