////////////////////////////////////////////////////////////////////// // // 5722-JV1 // (C) Copyright IBM Corp. 2000 // ////////////////////////////////////////////////////////////////////// // // File Name: SampleThreadSubjectLogin.java // // Class: SampleThreadSubjectLogin // ////////////////////////////////////////////////////////////////////// // // Code disclaimer information // This document contains programming examples. // // IBM grants you a nonexclusive copyright license to use all // programming code examples from which you can generate similar function // tailored to your own specific needs. // // All sample code is provided by IBM for illustrative purposes only. These // examples have not been thoroughly tested under all conditions. IBM, // therefore, cannot guarantee or imply reliability, serviceability, or // function of these programs. // // All programs contained herein are provided to you "AS IS" without any // warranties of any kind. The implied warranties of non-infringement, // merchantability and fitness for a particular purpose are expressly // disclaimed. ////////////////////////////////////////////////////////////////////// // // CHANGE ACTIVITY: // // // END CHANGE ACTIVITY // //////////////////////////////////////////////////////////////////// import com.ibm.security.auth.ThreadSubject; import com.ibm.as400.access.*; import java.io.*; import java.util.*; import java.security.Principal; import javax.security.auth.*; import javax.security.auth.callback.*; import javax.security.auth.login.*; /** * This SampleThreadSubjectLogin application authenticates a single * user, swaps the OS thread identity to the authenticated user, * and then writes "Hello World" into a privately authorized * file, thread.txt, in the user's test directory. * *

The user is requested to enter the user id and password to * authenticate. * * If successful, the user name and number of Credentials * are displayed. * * Setup and run instructions => 1) Make a new user, JAAS13, by invoking "CRTUSRPRF USRPRF(JAAS13) PASSWORD() TEXT('JAAS sample user id')" with *USER class authority. 2) Allocate a dummy test file, "/thread.txt", and privately grant JAAS13 *RWX authority to it for write access. 3) Copy SampleThreadSubjectLogin.java into your test directory. 4) Change the current directory to your test directory and compile the java source code.. Enter - strqsh cd ' Authentication Services. Select the link to the ProfileTokenCredential class. At the top select 'This Package' for the entire com/ibm/as400/security/auth Java package. Javadoc for the authentication classes can also be found by selecting 'Javadoc' => 'Access Classes' on the left frame. Select 'All Packages' at the top and look for the com.ibm.as400.security.* packages) ADDLNK OBJ('/QIBM/ProdData/OS400/jt400/lib/jt400Native.jar') NEWLNK('/QIBM/ProdData/Java400/jdk13/lib/ext/jt400Native.jar') ///////////////////////////////////// IMPORTANT NOTES - ///////////////////////////////////// Symbolically linking the jt400Native.jar file to the /QIBM/ProdData/Java400/jdk13/lib/ext directory above will force all JDK 1.3 users on the system to run with this version of jt400Native.jar. This may not be desirable if various users require different versions of IBM Toolbox for Java classes. Another option includes putting the jt400Native.jar in the application CLASSPATH as described above when invoking the application. For example change the CLASSPATH in step 10 - JAVA CLASS(SampleThreadSubjectLogin) CLASSPATH('/classes:/QIBM/ProdData/OS400/jt400/lib/jt400Native.jar') ... When updating the Java2 policy files for a real application remember to grant the appropriate permissions to the actual locations of IBM Toolbox for Java jar files. Even though they may be symbolically linked to the extension directories above which are granted java.security.AllPermission in the ${java.home}/lib/security/java.policy file, authorization is based on the actual location of the jar files. For example to successfully use the credential classes in the Java toolbox, you would add the below to your application's Java2 policy file - grant codeBase "file:/QIBM/ProdData/OS400/jt400/lib/jt400Native.jar" { permission javax.security.auth.AuthPermission "modifyThreadIdentity"; permission java.lang.RuntimePermission "loadLibrary.*"; permission java.lang.RuntimePermission "writeFileDescriptor"; permission java.lang.RuntimePermission "readFileDescriptor"; } You will also need to add the above permissions for the application's codeBase since the operations performed by IBM Toolbox for Java jar files do not run in privileged mode. This sample already grants the above permissions to all java classes by omitting the codeBase parameter in the threadJava2.policy file. 8) Make sure the Host Servers are started and running. The ProfileTokenCredential classes which reside in IBM Toolbox for Java, ie jt400Native.jar, are used as the credentials that are attached to the authenticated subject by the SampleThreadSubjectLogin.java program. The Toolbox for Java credential classes require access to the Host Servers. 9) Invoke SampleThreadSubjectLogin while signed on as a user that does not have access to '/thread.txt'. 10) Start the sample by entering the following CL commands => CHGCURDIR DIR('') JAVA CLASS(SampleThreadSubjectLogin) CLASSPATH('/classes') PROP((java.version '1.3') (java.security.manager) (java.security.auth.login.config '/threadLogin.config') (java.security.policy '/threadJava2.policy') (java.security.auth.policy '/threadJaas.policy')) Enter the user id and password when prompted from step 1. 11) Check /thread.txt for the "Hello World" entry. * **/ public class SampleThreadSubjectLogin { /** * Attempt to authenticate the user. * * @param args * Input arguments for this application (ignored). * */ public static void main(String[] args) { // use the configured LoginModules for the "AS400ToolboxApp" entry LoginContext lc = null; try { // if provided, the same subject will be used for multiple login attempts lc = new LoginContext("AS400ToolboxApp", new Subject(), new SampleCBHandler()); } catch (LoginException le) { le.printStackTrace(); System.exit(-1); } // the user has 3 attempts to authenticate successfully int i; for (i = 0; i < 3; i++) { try { // attempt authentication lc.login(); // if we return with no exception, authentication succeeded break; } catch (AccountExpiredException aee) { System.out.println("Your account has expired"); System.exit(-1); } catch (CredentialExpiredException cee) { System.out.println("Your credentials have expired."); System.exit(-1); } catch (FailedLoginException fle) { System.out.println("Authentication Failed"); try { Thread.currentThread().sleep(3000); } catch (Exception e) { // ignore } } catch (Exception e) { System.out.println("Unexpected Exception - unable to continue"); e.printStackTrace(); System.exit(-1); } } // did they fail three times? if (i == 3) { System.out.println("Sorry authentication failed"); System.exit(-1); } // display authenticated principals & credentials System.out.println("Authentication Succeeded"); System.out.println("Principals:"); Iterator itr = lc.getSubject().getPrincipals().iterator(); while (itr.hasNext()) System.out.println(itr.next()); itr = lc.getSubject().getPrivateCredentials().iterator(); while (itr.hasNext()) System.out.println(itr.next()); itr = lc.getSubject().getPublicCredentials().iterator(); while (itr.hasNext()) System.out.println(itr.next()); // let's do some Principal-based work: ThreadSubject.doAsPrivileged(lc.getSubject(), new java.security.PrivilegedAction() { public Object run() { System.out.println("\nYour java.home property: " +System.getProperty("java.home")); System.out.println("\nYour user.home property: " +System.getProperty("user.home")); File f = new File("thread.txt"); System.out.print("\nthread.txt does "); if (!f.exists()) System.out.print("not "); System.out.println("exist in your current directory"); try { // write "Hello World number x" into thread.txt PrintStream ps = new PrintStream(new FileOutputStream("thread.txt", true), true); long flen = f.length(); ps.println("Hello World number " + Long.toString(flen/22) + "\n"); ps.close(); } catch (Exception e) { e.printStackTrace(); } System.out.println("\nOh, by the way, " + SampleThreadSubjectLogin.getCurrentUser()); try { Thread.currentThread().sleep(2000); } catch (Exception e) { // ignore } System.out.println("\n\nHello World!\n"); return null; } }, null); System.exit(0); }// end main() // Returns the current OS identity for the main thread of the application. // (This routine uses classes from the IBM Toolbox for Java) // Note - Applications running on a secondary thread cannot use this API to determine the current user. static public String getCurrentUser() { try { AS400 localSys = new AS400("localhost", "*CURRENT", "*CURRENT"); int ccsid = localSys.getCcsid(); ProgramCall qusrjobi = new ProgramCall(localSys); ProgramParameter[] parms = new ProgramParameter[6]; int rLength = 100; parms[0] = new ProgramParameter(rLength); parms[1] = new ProgramParameter(new AS400Bin4().toBytes(rLength)); parms[2] = new ProgramParameter(new AS400Text(8, ccsid, localSys).toBytes("JOBI0600")); parms[3] = new ProgramParameter(new AS400Text(26,ccsid, localSys).toBytes("*")); parms[4] = new ProgramParameter(new AS400Text(16,ccsid, localSys).toBytes("")); parms[5] = new ProgramParameter(new AS400Bin4().toBytes(0)); qusrjobi.setProgram(QSYSObjectPathName.toPath("QSYS", "QUSRJOBI", "PGM"), parms); AS400Text uidText = new AS400Text(10, ccsid, localSys); // Invoke the QUSRJOBI API qusrjobi.run(); byte[] uidBytes = new byte[10]; System.arraycopy((qusrjobi.getParameterList())[0].getOutputData(), 90, uidBytes, 0, 10); return ((String)(uidText.toObject(uidBytes))).trim(); } catch (Exception e) { e.printStackTrace(); } return ""; } } //end SampleThreadSubjectLogin class /** * A CallbackHandler is passed to underlying security * services so that they may interact with the application * to retrieve specific authentication data, * such as user names and passwords, or to display certain * information, such as error and warning messages. * *

CallbackHandlers are implemented in an application * and platform-dependent fashion. The implementation decides * how to retrieve and display information depending on the * Callbacks passed to it. * *

This class provides a sample CallbackHandler for applications * running in an OS/400 environment. However, it is not intended * to fulfill the requirements of production applications. * As indicated, the CallbackHandler is ultimately considered to * be application-dependent, as individual applications have * unique error checking, data handling, and user * interface requirements. * *

The following callbacks are handled: *

* *

For simplicity, prompting is handled interactively via * standard input and output. However, it is worth noting * that when standard input is provided by the console this * approach will allow passwords to be viewed as they are * typed. This should be avoided in production * applications. * *

This CallbackHandler also allows a name and password * to be acquired through an alternative mechanism * and set directly on the handler to bypass the need for * user interaction on the respective Callbacks. * */ class SampleCBHandler implements CallbackHandler { private String name_ = null; private String password_ = null; /** * Constructs a new SampleCBHandler. * */ public SampleCBHandler() { this(null, null); } /** * Constructs a new SampleCBHandler. * *

A name and password can optionally be specified in * order to bypass the need to prompt for information * on the respective Callbacks. * * @param name * The default value for name callbacks. A null * value indicates that the user should be * prompted for this information. A non-null value * cannot be zero length or exceed 10 characters. * * @param password * The default value for password callbacks. A null * value indicates that the user should be * prompted for this information. A non-null value * cannot be zero length or exceed 10 characters. */ public SampleCBHandler(String name, String password) { if (name != null) if ((name.length()==0) || (name.length()>10)) throw new IllegalArgumentException("name"); name_ = name; if (password != null) if ((password.length()==0) || (password.length()>10)) throw new IllegalArgumentException("password"); password_ = password; } /** * Handle the given name callback. * *

First check to see if a name has been passed in * on the constructor. If so, assign it to the * callback and bypass the prompt. * *

If a value has not been preset, attempt to prompt * for the name using standard input and output. * * @param c * The NameCallback. * * @exception java.io.IOException * If an input or output error occurs. * */ private void handleNameCallback(NameCallback c) throws IOException { // Check for cached value if (name_ != null) { c.setName(name_); return; } // No preset value; attempt stdin/out c.setName( stdIOReadName(c.getPrompt(), 10)); } /** * Handle the given name callback. * *

First check to see if a password has been passed * in on the constructor. If so, assign it to the * callback and bypass the prompt. * *

If a value has not been preset, attempt to prompt * for the password using standard input and output. * * @param c * The PasswordCallback. * * @exception java.io.IOException * If an input or output error occurs. * */ private void handlePasswordCallback(PasswordCallback c) throws IOException { // Check for cached value if (password_ != null) { c.setPassword(password_.toCharArray()); return; } // No preset value; attempt stdin/out // Note - Not for production use. // Password is not concealed by standard console I/O if (c.isEchoOn()) c.setPassword( stdIOReadName(c.getPrompt(), 10).toCharArray()); else { // Note - Password is not concealed by standard console I/O c.setPassword(stdIOReadName(c.getPrompt(), 10).toCharArray()); } } /** * Handle the given text output callback. * *

If the text is informational or a warning, * text is written to standard output. If the * callback defines an error message, text is * written to standard error. * * @param c * The TextOutputCallback. * * @exception java.io.IOException * If an input or output error occurs. * */ private void handleTextOutputCallback(TextOutputCallback c) throws IOException { if (c.getMessageType() == TextOutputCallback.ERROR) System.err.println(c.getMessage()); else System.out.println(c.getMessage()); } /** *

Retrieve or display the information requested in the * provided Callbacks. * *

The handle method implementation * checks the instance(s) of the Callback * object(s) passed in to retrieve or display the * requested information. * * @param callbacks * An array of Callback objects provided * by an underlying security service which contains * the information requested to be retrieved or displayed. * * @exception java.io.IOException * If an input or output error occurs. * * @exception UnsupportedCallbackException * If the implementation of this method does not support * one or more of the Callbacks specified in the * callbacks parameter. * */ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i=0; imaxLength or by a newline. * * @param prompt * The text to display to standard output immediately * prior to reading the requested value. * * @param maxLength * Maximum length of the String to return. * * @return * The entered string. The value returned will * not contain leading or trailing whitespace * and is converted to uppercase. * * @exception IOException * If an input or output error occurs. * */ private String stdIOReadName(String prompt, int maxLength) throws IOException { stdIOPrompt(prompt); String s = (new BufferedReader (new InputStreamReader(System.in))).readLine().trim(); if (s.length() > maxLength) s = s.substring(0,maxLength); return s.toUpperCase(); } }//end SampleCBHandler class