* ===========================================================================

 * 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 client;

 * to be used in conjunction with the JGSS sample server.

 * The client first establishes a context with the server

 * and then sends wrapped message followed by a MIC to the server.

 * The MIC is calculated over the plain text that was wrapped.

 * The client requires to server to authenticate itself

 * (mutual authentication) during context establishment.

 * It also delegates its credentials to the server.

 * <p>

 * It sets the JAVA variable

 * <code>javax.security.auth.useSubjectCredsOnly</code> to <code>false</code>

 * so that JGSS will not acquire credentials through JAAS.

 * <p>

 * The client 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.

 * <p>

 * Usage: Client [options]

 * <p>

 * The -? option produces a help message including supported options.

 * <p>

 * This sample client does not use JAAS.

 * The client can be run against the JAAS sample client and server.

 * See {@link JAASClient JAASClient} for a sample client that uses JAAS.

 *

 * @author Thomas Owusu

 * @version %I%, %G%

 */

 

class Client

{

    private Util testUtil     = null;

    private String myName     = null;

    private GSSName gssName   = null;

    private String serverName = null;

    private int servicePort   = 0;

    private GSSManager mgr    = GSSManager.getInstance();

    private GSSName service   = null;

    private GSSContext context = null;

    private String program    = "Client";

    private String debugPrefix= "Client: ";

    private TCPComms tcp      = null;

    private String data       = null;

    private byte[] dataBytes  = null;

    private String serviceHostname= null;

    private GSSCredential gssCred = null;

    private boolean useStreams;

 

    private static Debug debug      = new Debug();

 

    private static final String usageString =

          "\t[-?] [-n name] [-s serverName]"

        + "\n\t[-h serverHost [:port]] [-p port] [-m msg]"

        + "\n"

        + "\n  -?\t\t\thelp; produces this message"

        + "\n  -n name\t\tthe client's principal name (without realm)"

        + "\n  -s serverName\t\tthe server's principal name (without realm)"

        + "\n  -h serverHost[:port]\tthe server's hostname"

        +         " (and optional port number)"

        + "\n  -p port\t\tthe port on which the server will be listening"

        + "\n  -m msg\t\tmessage to send to the server";

 

 

    // Caller must call initialize (may need to call processArgs first).

    public Client (String programName) throws Exception

    {

        testUtil = new Util();

        if (programName != null)

        {

            program = programName;

            debugPrefix = programName + ": ";

        }

    }

 

    // Caller must call initialize (may need to call processArgs first).

    Client (String programName, boolean useSubjectCredsOnly) throws Exception

    {

        this(programName);

        setUseSubjectCredsOnly(useSubjectCredsOnly);

    }

 

    public Client(GSSCredential myCred,

                  String serverNameWithoutRealm,

                  String serverHostname,

                  int serverPort,

                  String message,

                  boolean useStreams)

        throws Exception

    {

        if (useStreams) {

            this.useStreams = useStreams;

            debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

                                      + "Using streams");

        }

        testUtil = new Util();

 

        if (myCred != null)

        {

            gssCred = myCred;

        }

        else

        {

            throw new GSSException(GSSException.NO_CRED, 0,

                                       "Null input credential");

        }

 

        init(serverNameWithoutRealm, serverHostname, serverPort, message);

    }

 

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

 

            // 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(String myNameWithoutRealm,

                      String serverNameWithoutRealm,

                      String serverHostname,

                      int    serverPort,

                      String message) throws Exception

    {

        myName = myNameWithoutRealm;

        init(serverNameWithoutRealm, serverHostname, serverPort, message);

    }

 

    private void init(String serverNameWithoutRealm,

                      String serverHostname,

                      int    serverPort,

                      String message) throws Exception

    {

        // peer's name

        if (serverNameWithoutRealm != null)

        {

            this.serverName = serverNameWithoutRealm;

        }

        else

        {

            this.serverName = testUtil.getDefaultServicePrincipalWithoutRealm();

        }

        debug.out(Debug.OPTS_CAT_APPLICATION,

                       debugPrefix + "server name=" + this.serverName);

 

        // peer's host

        if (serverHostname != null)

        {

            this.serviceHostname = serverHostname;

            debug.out(Debug.OPTS_CAT_APPLICATION,

                       debugPrefix + "server host=" + this.serviceHostname);

        }

        else

        {

            debug.out(Debug.OPTS_CAT_APPLICATION,

                       debugPrefix + "server host=<local host>");

 

            //this.serviceHostname = testUtil.getDefaultServiceHostname();

        }

 

        // peer's port

        if (serverPort > 0)

        {

            this.servicePort = serverPort;

        }

        else

        {

            this.servicePort = testUtil.getDefaultServicePort();

        }

        debug.out(Debug.OPTS_CAT_APPLICATION,

                       debugPrefix + "server port=" + this.servicePort);

 

        // message for peer

        if (message != null)

        {

            this.data = message;

        }

        else

        {

            this.data = "I think. Therefore I am?";

        }

 

        this.dataBytes = this.data.getBytes();

 

        tcp = new TCPComms(serviceHostname, servicePort);

    }

                  

 

    void initialize() throws Exception

    {

        Oid krb5MechanismOid = new Oid("1.2.840.113554.1.2.2");

 

        if (gssCred == null)

        {

            if (myName != null)

            {

                debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

                                   + "creating GSSName USER_NAME for "

                                   + myName);

 

                gssName = mgr.createName(

                               myName,

                               GSSName.NT_USER_NAME,

                               krb5MechanismOid);

                               //GSSManagerImpl.MECH_TYPE_KRB5);

 

                debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

                                   + "Canonicalized GSSName=" + gssName);

            }

            else

                gssName = null; // for default credentials

 

 

            debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "creating"

               + ((gssName == null)? " default " : " ")

               + "credential");

 

            gssCred = mgr.createCredential(

                                    gssName,

                                    GSSCredential.DEFAULT_LIFETIME,

                                    (Oid)null,

                                    GSSCredential.INITIATE_ONLY);

            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();

            lifetime = gssCred.getRemainingInitLifetime(mechs[0]);

            debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

                  + "Init lifetime remaining on cred for mech " + mechs[0]

                     + ": " + lifetime + " secs");

        }

 

        debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + gssCred);

  

        String serviceStr = serverName;

        if (serviceHostname != null) {

            serviceStr += "@" + serviceHostname;

        }

 

        debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

           + "creating canonicalized GSSName for serverName " + serviceStr);

 

        service = mgr.createName(serviceStr,

                                 GSSName.NT_HOSTBASED_SERVICE,

                                 krb5MechanismOid);

                                 //GSSManagerImpl.MECH_TYPE_KRB5);

 

        debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

           + "Canonicalized server name = " + service);

 

        debug.out(Debug.OPTS_CAT_APPLICATION,

                            debugPrefix + "Raw data=" + data);

    }

 

 

    void establishContext(BitSet flags) throws Exception

    {

        try {

 

          debug.out(Debug.OPTS_CAT_APPLICATION,

                            debugPrefix + "creating GSScontext");

 

          Oid defaultMech = null;

          context = mgr.createContext(service, defaultMech, gssCred,

                                      GSSContext.INDEFINITE_LIFETIME);

 

          if (flags != null)

          {

              if (flags.get(Util.CONTEXT_OPTS_MUTUAL))

              {

                  debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

                       + "requesting mutualAuthn");

 

                  context.requestMutualAuth(true);

              }

 

              if (flags.get(Util.CONTEXT_OPTS_INTEG))

              {

                  debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

                       + "requesting integrity");

 

                  context.requestInteg(true);

              }

 

              if (flags.get(Util.CONTEXT_OPTS_CONF))

              {

                  context.requestConf(true);

                  debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

                          + "requesting confidentiality");

              }

 

              if (flags.get(Util.CONTEXT_OPTS_DELEG))

              {

                  context.requestCredDeleg(true);

                  debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

                           + "requesting delegation");

              }

 

              if (flags.get(Util.CONTEXT_OPTS_REPLAY))

              {

                  context.requestReplayDet(true);

                  debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

                      + "requesting replay detection");

              }

 

              if (flags.get(Util.CONTEXT_OPTS_SEQ))

              {

                  context.requestSequenceDet(true);

                  debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

                           + "requesting out-of-sequence detection");

              }

              // Add more later!

          }

 

          byte[] response = null;

          byte[] request = null;

          int len = 0;

          boolean done = false;

          ByteArrayInputStream bis = null;

          ByteArrayOutputStream bos = null;

 

          do {

              debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

                  + "Calling initSecContext");

 

              if (!useStreams) {

                  request = context.initSecContext(response, 0, len);

              } else {

                  bos = new ByteArrayOutputStream();

                  if (response == null) {

                      context.initSecContext((InputStream)null, bos);

                  } else {

                      bis = new ByteArrayInputStream(response);

                      context.initSecContext(bis, bos);

                  }

                  request = bos.toByteArray();

              }

 

              if (request != null && request.length != 0)

              {

                  debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix

                    + "Sending context token");

 

                  tcp.send(request);

              }

              done = context.isEstablished();

 

              if (!done)

              {

                  debug.out(Debug.OPTS_CAT_APPLICATION,

                      debugPrefix + "Receiving response token");

 

                  byte[] temp = tcp.receive();

                  response = temp;

                  len = response.length;

              }

          } while(!done);

 

          if (debug.on(Debug.OPTS_CAT_APPLICATION)) {

              String msg = "established context with acceptor "

                                              + context.getTargName();

              debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + msg);

 

              boolean delegationState = context.getCredDelegState();

              msg = "getCredDelegState=" + delegationState;

              debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + msg);

          }

         

        } catch (Exception exc) {

            exc.printStackTrace();

            throw exc;

        }

    }

 

    void doMIC() throws Exception

    {

        debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "generating MIC");

        byte[] mic = null;

        if (!useStreams) {

            mic = context.getMIC(dataBytes, 0, dataBytes.length, null);

        } else {

            ByteArrayInputStream bis = new ByteArrayInputStream(dataBytes);

            ByteArrayOutputStream bos = new ByteArrayOutputStream();

            context.getMIC(bis, bos, null);

            mic = bos.toByteArray();

        }

 

        if (mic != null)

        {

            debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "sending MIC");

            tcp.send(mic);

        }

        else

            debug.out(Debug.OPTS_CAT_APPLICATION,

                             debugPrefix + "getMIC Failed");

    }

 

    void doWrap() throws Exception

    {

        MessageProp mp = new MessageProp(true);

        mp.setPrivacy(context.getConfState());

 

        debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "wrapping message");

 

        byte[] wrapped = null;

        if (!useStreams) {

            wrapped = context.wrap(dataBytes, 0, dataBytes.length, mp);

        } else {

            ByteArrayInputStream bis = new ByteArrayInputStream(dataBytes);

            ByteArrayOutputStream bos = new ByteArrayOutputStream();

            context.wrap(bis, bos, mp);

            wrapped = bos.toByteArray();

        }

 

        if (wrapped != null)

        {

            debug.out(Debug.OPTS_CAT_APPLICATION,

                    debugPrefix + "sending wrapped message");

 

            tcp.send(wrapped);

        }

        else

            debug.out(Debug.OPTS_CAT_APPLICATION, debugPrefix + "wrap Failed");

    }

 

    void printUsage()

    {

        System.out.println(program + usageString);

    }

 

 

    void processArgs(String[] args) throws Exception {

        String port           = null;

        String myName         = null;

        int servicePort       = 0;

        String serviceHostname      = null;

 

        String sHost = null;

        String msg = null;

 

        int ch = -1;

 

        if (args != null && args.length > 0) {

            for (int i=0; i<args.length; i++) {

                if ("-?".equals(args[i])) {

                    printUsage();

                    System.exit(1);

                } 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 (port == null)

                                port = sHost.substring(p+1,

                                                     sHost.length()).trim();

                            sHost = temp1;

                        }

                        continue;

                    } else {

                        duplicateOptionError("-h");

                    }

 

                } 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 ("-m".equals(args[i])) {

                    if (msg == null) {

                        if (i+1 >= args.length) {

                            noArgForOptionError("-m");

                        }

                        msg = args[++i];

                        continue;

                    } else {

                        duplicateOptionError("-m");

                    }

 

                } else if ("-n".equals(args[i])) {

                    if (myName == null) {

                        if (i+1 >= args.length) {

                            noArgForOptionError("-n");

                        }

                        myName = args[++i];

                        continue;

                    } else {

                        duplicateOptionError("-n");

                    }

 

                } else if ("-s".equals(args[i])) {

                    if (serverName == null) {

                        if (i+1 >= args.length) {

                            noArgForOptionError("-n");

                        }

                        serverName = args[++i];

                        continue;

                    } else {

                        duplicateOptionError("-s");

                    }

                }

            }

        }

       

        if ((port != null) && (port.length() > 0))

        {

            int p = -1;

            try {

                p = Integer.parseInt(port);

            } catch (Exception exc) {

                System.out.println("Bad port input: "+port);

            }

 

            if (p != -1)

                servicePort = p;

        }

      

        if ((sHost != null) && (sHost.length() > 0)) {

                serviceHostname = sHost;               

        }

 

        init(myName, serverName, serviceHostname, servicePort, msg);

    }

 

    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 interactWithAcceptor(BitSet flags) throws Exception

    {

        establishContext(flags);

        doWrap();

        doMIC();

    }

 

    void interactWithAcceptor() throws Exception

    {

        BitSet flags = new BitSet();

        flags.set(Util.CONTEXT_OPTS_MUTUAL);

        flags.set(Util.CONTEXT_OPTS_CONF);

        flags.set(Util.CONTEXT_OPTS_INTEG);

        flags.set(Util.CONTEXT_OPTS_DELEG);

        interactWithAcceptor(flags);

    }

 

    void dispose() throws Exception

    {

        if (tcp != null)

        {

            tcp.close();

        }

    }

  

    public static void main(String args[]) throws Exception

    {

        System.out.println(debug.toString()); // XXXXXXX

        String programName = "Client";

        Client client = null;

        try {

            client = new Client(programName,

                                false); // don't use Subject creds.

            client.processArgs(args);

            client.initialize();

            client.interactWithAcceptor();

        } catch (Exception exc) {

            debug.out(Debug.OPTS_CAT_APPLICATION,

                            programName + " Exception: " + exc.toString());

            exc.printStackTrace();

            throw exc;

        } finally {

            try {

                if (client != null)

                    client.dispose();

            } catch (Exception exc) {}

        }

 

        debug.out(Debug.OPTS_CAT_APPLICATION, programName + ": done");

    }

}