TopsecurityClient.java
package dk.topsecurity.client; import java.rmi.Naming; import dk.topsecurity.common.TopsecurityLoginInterface; import dk.topsecurity.common.TopsecurityServerInterface; /** * Test client performing authentication and testing authorisation. */ public class TopsecurityClient { static boolean debug = true; /** * Simple console trace to system.out for debug purposes only.&Ltp> * * @param msg the message to be printed to the console */ private static void debugOut(String msg) { if( debug ) System.out.println("\t[TopsecurityClient] " + msg); } /** * Requires user arguments from commandline and attempts to * authenticate the user and authorize to perform * a priveleged action.&Ltp> * * @param args Commandline input arguments */ public static void main(String[] args) { String server,user,pass; if (args.length!=2 && args.length!=3) { System.out.println("TopsecurityClient &Ltusername password> [server]"); } else { if (args.length==2) { user = args[0]; pass = args[1]; server = "127.0.0.1:6000"; } else { user = args[0]; pass = args[1]; server = args[3]; } //connect to the server through RMI String serverObject = "rmi://" + server + "/" + "TopsecurityRemoteLoginServer"; debugOut("connecting to server="+serverObject); TopsecurityLoginInterface loginServer = null; try { //authenticate credentials loginServer = (TopsecurityLoginInterface) Naming.lookup(serverObject); //login object received } catch (Exception e) { System.out.println(e); System.exit(0); } //do the login try { debugOut("ATTEMPTING LOGIN"); TopsecurityServerInterface theServer = (TopsecurityServerInterface) loginServer.login(user, pass); debugOut("LOGIN SUCCESSFULL"); //perfom all priviledged operations try { debugOut("ATTEMPTING 1st Operation"); theServer.doOperationA(); debugOut("SUCCESSFULL 1st Operation"); } catch (Exception e) { System.out.println(e); } try { debugOut("ATTEMPTING 2nd Operation"); theServer.doOperationB(); debugOut("SUCCESSFULL 2nd Operation"); } catch (Exception e) { System.out.println(e); } } catch (Exception e) { debugOut("error : "+e); e.printStackTrace(); System.exit(0); } } } } |
loginServer.login
executes TopsecurityLoginImpl.login
.TopsecurityLoginImpl.java
. It creates the LoginContext known from
previous client examples, performs a full JAAS login according to permissions
in policy file for the server. Obtains the subject to be used later for
checking authorization to the methods, which the client might want to execute.TopsecurityLoginImpl.java
package dk.topsecurity.server; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import dk.topsecurity.common.TopsecurityLoginInterface; import dk.topsecurity.common.TopsecurityServerInterface; /** * Implements the server object that allows clients to login. */ public class TopsecurityLoginImpl extends java.rmi.server.UnicastRemoteObject implements TopsecurityLoginInterface { /** The real server object */ private TopsecurityServerInterface myServer; /** * Class constructor. * * @param theServer The real server object. */ public TopsecurityLoginImpl(TopsecurityServerInterface theServer) throws java.rmi.RemoteException { myServer = theServer; } /** * Allows a client to login and get an interface to the server. */ public TopsecurityServerInterface login(String username, String password) throws java.rmi.RemoteException, LoginException { LoginContext lc = new LoginContext("TopsecurityJAASLogin", new RemoteCallbackHandler(username, password)); lc.login(); Subject user = lc.getSubject(); // Return a reference to a proxy object that encapsulates the access // to the server, for this client return new TopsecurityServerProxy(user, myServer); } } class RemoteCallbackHandler implements CallbackHandler { private String username; private String password; RemoteCallbackHandler(String username, String password){ this.username = username; this.password = password; } public void handle(Callback[] cb) { for (int i = 0; i < cb.length; i++){ if (cb[i] instanceof NameCallback){ NameCallback nc = (NameCallback)cb[i]; nc.setName(username); } else if (cb[i] instanceof PasswordCallback){ PasswordCallback pc = (PasswordCallback)cb[i]; pc.setPassword(password.toCharArray()); password = null; } } } } |
TopsecurityLoginModule.java
implementation.TopsecurityLoginModule.java
..completely the same compared to previous example... |
TopsecurityLogin.config
..completely the same compared to previous example... |
LoginContext
.
As usual, TopsecurityLoginPrincipal is a simple class derived from
java.security.Principal
. A TopsecurityServerProxy
object containing a reference to login credentials is created and returned to
the client.TopsecuritySecurity.policy
: addressing code permissions...
Allows code in the built .jar files to perform actions necessary. This
policy file is assiated with the environment variable java.security.policy
TopsecurityAuthorization.policy
: addressing principal permissions...
Restricts which (authenticated) users may call which methods on the
server.This policy file is assiated with the environment
variable java.security.auth.policy
javax.security.auth.Subject.doAs
method, which allows a piece
of code to be executed with the privileges of a specific principal. The piece
of code must be an object, which implements either
java.security.PrivilegedAction
or
java.security.PrivilegedExceptionAction
(latter allows retrieval
of exception thrown during the .run(..)
method). In all cases,
the .run(..)
method is executed with privileges as
the user
.TopsecurityServerProxy.java
package dk.topsecurity.server; import javax.security.auth.Subject; import dk.topsecurity.common.TopsecurityServerInterface; import dk.topsecurity.permissionvalidation.TopsecurityMethodcallValidate; import java.security.AccessController; import java.security.PrivilegedExceptionAction; import dk.topsecurity.permissions.TopsecurityServerPermission; /** * Proxy imlementing server interface. * * Protects the server from direct calls by clients. All calls * passes security check and the server implementation is not * visible for the client. */ public class TopsecurityServerProxy extends java.rmi.server.UnicastRemoteObject implements TopsecurityServerInterface { /** A reference to the real server object */ private TopsecurityServerInterface theServer; /** The user associated with this proxy */ private Subject theUser; /** * Class constructor. Saves subject generated during JAAS * .login() and a reference to the active server. * * @param user A subject representing the user for this proxy. * @param theServer The real server object. */ public TopsecurityServerProxy(Subject user, TopsecurityServerInterface theServer) throws java.rmi.RemoteException { this.theServer = theServer; this.theUser = user; } /** * Proxy implementation of (1st) method in the server interface. * * The client calls this method. If he client has the * appropriate permissions, the call goes through. */ public void doOperationA() throws java.rmi.RemoteException, SecurityException { checkPermission("doOperationA"); theServer.doOperationA(); } /** * Proxy implementation of (2nd) method in the server interface. * * The client calls this method. If he client has the * appropriate permissions, the call goes through. */ public void doOperationB() throws java.rmi.RemoteException, SecurityException { checkPermission("doOperationB"); theServer.doOperationB(); } /** * Check if the current client can call a certain method. * The check is made through JAAS and its policy file. * * @param methodName The method that will be called. * @throws SecurityException If the client doesn't have the necessary * permissions. */ private void checkPermission(String methodName) throws SecurityException { // Assume the identity of the user, and validate if he can // call this method try { Subject.doAs(theUser, new TopsecurityMethodcallValidate(methodName)); } catch (java.security.PrivilegedActionException e) { throw (SecurityException) e.getException(); } } } |
javax.security.auth.Subject.doAs(user, new ValidateMethodCall(methodName))
is
performed, the .run(..)
method in ValidateMethodCall
is executed with privileges for user
as specified in
policy file java.security.auth.policy
. The method .run(..)
does a java.security.AccessController.checkPermission(..)
check on
the permission to execute a method name. This check consults the policy file
and cause an exception for no privileges. java.security.AccessController
performs security checks according to the current context. When installing
a security manager in Java, calls are normally propagated to the
AccessController class.
ValidateMethodCall.java
package dk.topsecurity.permissionvalidation; import java.security.AccessController; import java.security.PrivilegedExceptionAction; import dk.topsecurity.permissions.TopsecurityServerPermission; /** * Assures that the a certain method can be called in the context * of the running code. */ public class TopsecurityMethodcallValidate implements PrivilegedExceptionAction { /** The method to be called */ private String priveledgedMethodName; /** * Make sure that the current user (defined by its context) has * the permissions to call the "methodName" method. For checking * this, a ServerPermission is required. * * authrmi.permissions.ServerPermission "methodName" * -> authorizes the call of a certain method * authrmi.permissions.ServerPermission "*" * -> authorizes the call of all methods */ public TopsecurityMethodcallValidate(String methodName) { priveledgedMethodName = methodName; } public Object run() { // Only has to check if the appropriate ServerPermission is owned by // the user. If not an exception is thrown. AccessController.checkPermission(new TopsecurityServerPermission(priveledgedMethodName)); return null; } } |
TopsecurityServerPermission
represents permissions. The
class extends java.security.BasicPermission
. When JAAS is
parsing the policy file, it automatically instantiates objects of this class
for representing the permissions for users.TopsecurityServerPermission.java
package dk.topsecurity.permissions; /** * Permission to call a method on the server. * * The functionality is already provided in the base class * java.security.BasicPermission. Thus, all that this class * is doing is serving as a type name. * * Permissions to call methods are specified as follows: * * authrmi.permissions.ServerPermission "methodName" * -> authorizes the call of a certain method * authrmi.permissions.ServerPermission "*" * -> authorizes the call of all methods */ public class TopsecurityServerPermission extends java.security.BasicPermission { /** * Creates a permission with a name. */ public TopsecurityServerPermission(String name) { super(name); } /** * Creates a permission with a name and an action string. * The action string is not used, but this constructor must exist * so that the policy file parser works. */ public TopsecurityServerPermission(String name, String actions) { super(name, actions); } } |
.checkPermissions(..)
succeeds, the actually priviledged
methods (.doOperation
) are called in the server implementation:
TopsecurityServerImpl.java
package dk.topsecurity.server; import dk.topsecurity.common.TopsecurityServerInterface; /** * The actual implementation of the server. */ public class TopsecurityServerImpl implements TopsecurityServerInterface { /** * The first priviledged operation. */ public void doOperationA() { System.out.println("Operation A!"); } /** * The second priviledged operation. */ public void doOperationB() { System.out.println("Operation B!"); } } |
.doOperationX
method on the proxy. Before
priviledged code are actually executed on the server, a check
Subject.doAs(theUser, new TopsecurityMethodcallValidate(".doOperationX"))
is performed on the server. This check performs a
AccessController.checkPermission(new TopsecurityServerPermission(".doOperationX"))
in the context of the user - checking permissions in policy file. If check
fails, an exception is thrown and the actual priviledged code is never reached.TopsecurityServer.java
package dk.topsecurity.server; import java.rmi.RMISecurityManager; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.util.Date; import dk.topsecurity.common.TopsecurityLoginInterface; import dk.topsecurity.common.TopsecurityServerInterface; /** * The main class for the server. */ public class TopsecurityServer { public static void main(String[] args) { /* Ensures that the identifiers generated for the server objects * will be secure */ System.setProperty("java.rmi.server.randomIDs", "true"); /* Binds the server object with the login interface to the registry */ try { TopsecurityServerInterface theServer = new TopsecurityServerImpl(); TopsecurityLoginInterface loginObject = new TopsecurityLoginImpl(theServer); Registry loginRegistry = LocateRegistry.createRegistry(6000); loginRegistry.bind("TopsecurityRemoteLoginServer", loginObject); System.out.println((new Date()) + ": Server up and running"); } catch (Exception e) { e.printStackTrace(); } } } |
TopsecurityLoginInterface.java
package dk.topsecurity.common; import javax.security.auth.login.LoginException; /** * Client interface for for server login. */ public interface TopsecurityLoginInterface extends java.rmi.Remote { /** * Method allowing login and, returning interface to the server. * * @param username The name of the user. * @param password The password of the user. * @return A reference to a proxy of the server object. * @throws SecurityException If the client is not allowed to login. */ public TopsecurityServerInterface login(String username, String password) throws java.rmi.RemoteException, LoginException; } |
TopsecurityServerInterface.java
package dk.topsecurity.common; /** * Server interface */ public interface TopsecurityServerInterface extends java.rmi.Remote { /** * 1st priviledged operation * * @throws SecurityException when client doesn't have proper permissions * for executing this method. */ public void doOperationA() throws java.rmi.RemoteException, SecurityException; /** * 2nd priviledged operation. * * @throws SecurityException when client doesn't have sufficient permissions * for executing this method. */ public void doOperationB() throws java.rmi.RemoteException, SecurityException; } |
build.xml
<project name="javacert" default="all" basedir=".\"> <target name="clean"> <delete quiet="true" dir=".\build"/> <delete quiet="true" dir=".\javadocs"/> <mkdir dir="./build"/> </target> <target name="compile"> <javac srcdir="./source" destdir=".\build" classpath="dk"/> <rmic classname="dk.topsecurity.server.TopsecurityLoginImpl" base="./build" classpath="."/> <rmic classname="dk.topsecurity.server.TopsecurityServerProxy" base="./build" classpath="."/> </target> <target name="generatedocs"> <javadoc destdir="javadoc" author="true" version="true" use="true" windowtitle="API"> <fileset dir="./source" defaultexcludes="yes" include="client/**/*.java common/**/*.java server/**/*.java"/> <doctitle><![CDATA[<h1>SunJavaDeveloperCertificationAssignment<br>FlyByNight project</h1>]]></doctitle> <bottom><![CDATA[<i>Generated 2003 by StigValentini for the Sun Java Developer Certification © Sun Corp. </i>]]></bottom> <tag name="todo" scope="all" description="To do:" /> <group title="Client Package" packages="suncertify.client.*"/> <group title="Server Package" packages="suncertify.server.*"/> <group title="Database Package" packages="suncertify.db.*"/> <link offline="true" href="http://java.sun.com/products/jdk/1.2/docs/api/" packagelistLoc="C:\tmp"/> <link href="http://developer.java.sun.com/developer/products/xml/docs/api/"/> </javadoc> </target> <target name="package"> <jar destfile="./actions.jar" basedir="./build" includes="dk\topsecurity\permissionvalidation\*.class"/> <jar destfile="./client.jar" basedir="./build" includes="dk\topsecurity\client\*.class"/> <jar destfile="./server.jar" basedir="./build" includes="dk\topsecurity\server\*.class"/> <jar destfile="./server.jar" basedir="./build" includes="dk\topsecurity\permissions\*.class" update="true"/> <jar destfile="./common.jar" basedir="./build" includes="dk\topsecurity\common\*.class"/> <jar destfile="./server_stub.jar" basedir="./build" includes="dk\topsecurity\server\*_Stub.class"/> </target> <target name="all" depends="clean,compile,package"> </target> </project> |