|
Tech Tips Archive
July 27, 2001
WELCOME to the Java Developer Connection (JDC) Tech Tips, July 27, 2001. This issue covers the Java Authentication and Authorization Service (JAAS). It introduces some key concepts in JAAS and shows you how to make use of these concepts. The tips are:
In order to run the code in this tip you will need access to JAAS version 1.0, which is available at: http://java.sun.com/products/jaas
These tips were developed using Java 2 SDK, Standard Edition,
v 1.3.
This issue of the JDC Tech Tips is written by Stuart Halloway, a Java specialist at DevelopMentor.
INTRODUCTION TO JAAS
The Java Authentication and Authorization Service (JAAS) extends the Java security model to perform checks based on the identity of the caller. Before you use JAAS, you need to understand some key concepts. These are:
-
Subject
-
Principal
-
Client and LoginContext
-
Configuration File
-
Provider
-
Java Policy File
-
JAAS Policy File
Subject
In JAAS, a subject is some identity in a system that you want to authenticate and to which you want to assign permissions. For example, a subject can be a human user, a machine, or a process. Subjects are represented by the javax.security.auth.Subject class.
Principal
Just as in the real world, a Subject can have relationships with several different authorities. For example, you might have one password and set of privileges at your bank, and a different password and set for of privileges for your voice mail. In this example, the authorities are the bank and the voice mail system. In JAAS, a Subject's multiple interactions with authorities are are represented by classes that implement the java.security.Principal interface. So a Principal is a class that represents a subject's interactions with an authority. A Principal simply knows its name and provides sensible overrides for the Object methods, as the following SimplePrincipal class demonstrates.
Note: You'll use this class and other components in an application that uses JAAS. Don't try to compile and use this class individually. Instructions for building and running the application are in the tip that follows this one, titled "Using JAAS."
import java.security.Principal;
public final class SimplePrincipal implements
Principal {
private final String name;
public SimplePrincipal(String name) {
if (name == null) {
throw new IllegalArgumentException(
"Name cannot be null");
}
this.name = name;
}
public int hashCode() {
return name.hashCode();
}
public java.lang.String getName() {
return name;
}
public java.lang.String toString() {
return "SimplePrincipal: " + name;
}
public boolean equals(java.lang.Object obj) {
if (obj == null) return false;
if (!(obj instanceof SimplePrincipal))
return false;
SimplePrincipal other =
(SimplePrincipal) obj;
return name.equals(other.getName());
}
}
|
Client and LoginContext
In order to associate a Principal with a Subject, clients must login. JAAS provides a concrete class, LoginContext, that acts as a session with a group of one or more authentication providers. The following JAASClient class demonstrates using a LoginContext:
import java.util.Iterator;
import java.security.PrivilegedAction;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
public class JAASClient {
public static void main(String [] args) {
try {
loginAndDoSomething();
}
catch (Exception e) {
e.printStackTrace();
}
}
public static void loginAndDoSomething() throws
Exception {
LoginContext ctx = new
LoginContext("SimpleLogin");
ctx.login();
Subject subj = ctx.getSubject();
System.out.println("Login assigned these
principals: ");
Iterator it = subj.getPrincipals().iterator();
while (it.hasNext())
System.out.println("\t" + it.next());
Subject.doAs(subj, new PrivilegedAction() {
public Object run() {
System.out.println("You live at " +
System.getProperty("user.home"));
return null;
}
});
ctx.logout();
}
}
|
The LoginContext constructor prepares to authenticate based on a named configuration. In this case, the configuration is named SimpleLogin (more on this in a moment). The call to login then causes the LoginContext to call one or more authentication providers. The call to getSubject returns a Subject that contains any Principals that the authenticators chose to assign. The doAs method then attempts a secured operation, which will succeed if one of the Principals has the appropriate Permission.
The call to getPrincipals is for debugging; it prints the Principals that login assigned to the Subject.
Configuration File
The LoginContext finds the named configuration from a
configuration file that looks like this:
//File conf/simple.conf
SimpleLogin {
SimpleLoginModule required;
};
This file tells the LoginContext to load a class named SimpleLoginModule, which is a service provider for some authentication strategy. The "required" means that this class's approval is necessary for login to succeed. The LoginContext allows multiple authentication providers to be used in tandem, in which case, additional entries would appear inside the SimpleLogin block.
Provider
A JAAS authentication provider provides the authentication strategy. In JAAS, an authentication strategy is implemented through the LoginModule interface. So the SimpleLoginModule must implement the LoginModule interface:
package java.security.auth.spi;
public interface LoginModule {
boolean abort();
boolean commit();
void initialize(Subject s, CallbackHandler ch,
Map shared, Map options);
boolean login();
boolean logout();
}
|
The LoginContext will call these methods in the following sequence:
-
initialize gives the service provider a Subject, to which it
can later attach Principals to if login succeeds. The
CallbackHandler is a client-provided set of interfaces for
entering authentication information. These interfaces decouple
the service provider from the particular input devices being
used, such as a computer keyboard or cell phone. The shared
map contains general configuration information, and the
options map contains a provider-specific configuration.
-
login tells the provider to authenticate the Subject.
Principals are not assigned at this time.
-
commit tells the provider that the LoginContext accepts the
results from all the other providers, and so the provider can
assign Principals.
-
abort tells the provider that even though it may have
authenticated the Subject, some other Provider failed and the
overall login should fail. The provider forgets that it ever
saw the login succeed and does not assign Principals to the Subject.
-
logout cancels the assignment of Principals to the Subject.
Now let's look at the content of SimpleLoginModule. In this
example, it provides a rudimentary implementation of LoginModule
that tests the user's knowledge of movie trivia:
import java.io.*;
import java.util.*;
import java.security.Principal;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.spi.LoginModule;
public class SimpleLoginModule implements
LoginModule {
private static final int NOT_AUTHENTICATED = 0;
private static final int AUTHENTICATED = 1;
private static final int
AUTHENTICATE_COMMITTED = 2;
private int state;
private SimplePrincipal sp;
private Subject sub;
public boolean abort() {
if ((sub != null) && (sp != null)) {
Set prins = sub.getPrincipals();
if (prins.contains(sp)) {
prins.remove(sp);
}
}
sub = null;
sp = null;
state = NOT_AUTHENTICATED;
return true;
}
public boolean commit() {
if (state < authenticated) {
return false;
}
if (sub == null) {
return false;
}
set prins = sub.getprincipals();
if (!prins.contains(sp)) {
prins.add(sp);
}
state = authenticate_committed;
return true;
}
public void initialize(subject s,
callbackhandler ch,
map shared, map options) {
state = not_authenticated;
sp = null;
sub = s;
}
public boolean login() {
bufferedreader br = new bufferedreader(
new inputstreamreader(system.in));
system.err.println("What can Jack Burton see? ");
try {
string resp = br.readline();
if (!resp.equalsignorecase("things
no one else can see")) {
return false;
}
}
catch (ioexception ioe) {
return false;
}
sp = new simpleprincipal("Jack");
state = authenticated;
return true;
}
public boolean logout() {
state = not_authenticated;
sp = null;
sub = null;
return true;
}
}
|
The SimpleLogin module is indeed very simple, recognizing only one Principal ("Jack") and only one authentication string, both of which are hard-coded.
Java Policy File
A JAAS authentication provider is a highly-trusted part of the system, and needs special permissions. To grant these permissions, you will need to specify a Java 2 policy file like this one:
//file conf/JAASProvider.policy
//trust the provider
grant codeBase "file:./provider/" {
permission java.security.AllPermission;
};
//trust JAAS
grant codeBase "file:/java/jaas1_0/lib/jaas.jar" {
permission java.security.AllPermission;
};
//these permissions are needed by the client
grant codeBase "file:./client/" {
permission javax.security.auth.AuthPermission
"createLoginContext";
permission
javax.security.auth.AuthPermission "doAs";
permission java.util.PropertyPermission
"user.home", "read";
};
|
Make sure to edit the jaas.jar codeBase entry to point to the location of jaas.jar on your local system. The other URLs are relative and do not need to be edited.
The provider files and the JAAS API files are completely trusted. The client code has the permissions it needs to bootstrap a LoginContext and to perform an authentication check. The client can also read the "user.home" property, which is the "sensitive operation" being demonstrated in this example.
JAAS Policy File
JAAS defines a policy file format for assigning permissions to authenticated clients. The format looks very similar to the normal Java 2 policy file. In fact, Java 2 SDK version 1.4 will support both formats from a single file. For JAAS 1.0, the JAAS-specific sections are in a separate file that looks like this:
//file conf/SimpleJAAS.policy
grant Principal SimplePrincipal "Jack" {
permission java.util.PropertyPermission
"user.home", "read";
};
|
The permission settings look exactly like Java 2 security permission settings. The only difference is in the grant declaration, which can list a Principal instead of, or in addition to, the normal codeBase and signedBy settings. This simple example gives Jack permission to read the user.home property.
In the 1.4 version of the Java 2 SDK, a separate JAAS policy file will not be necessary. All settings will be controlled by the standard policy.
USING JAAS
Let's build and run a JAAS application using the components described in the "Introduction to JAAS," above. All you need to do is start the client in a virtual machine with the correct configuration. Specifically, you:
-
Compile the Java classes. Remember to first ensure that
your class path includes the
jaas.jar file. Compile the client
class (JAASClient) to a subdirectory named "client." Compile
all the other classes to a subdirectory named "provider."
-
Place the configuration file (
simple.conf) in a subdirectory
named "conf."
-
Place the policy files (
JAASProvider.policy and
SimpleJAAS.policy) in the "conf" subdirectory.
With those files in place, issue the following command. Make sure to edit the command to specify the correct location of jaas.jar.
On Windows systems (this is shown in multiple lines for readibility. You should enter the command as a single line):
java -cp client;provider;d:\java\jaas1_0\lib\jaas.jar
-Djava.security.manager
-Djava.security.policy=conf/JAASProvider.policy
-Djava.security.auth.policy=conf/SimpleJAAS.policy
-Djava.security.auth.login.config=conf/simple.conf
JAASClient
|
On UNIX systems:
java -cp client:provider:/java/jaas1_0/lib/jaas.jar \
-Djava.security.manager \
-Djava.security.policy=conf/JAASProvider.policy \
-Djava.security.auth.policy=conf/SimpleJAAS.policy \
-Djava.security.auth.login.config=conf/simple.conf \
JAASClient
|
If you examine the command, you'll see that it:
-
Turns on Java 2 security
(
-Djava.security.manager)
-
Gives the provider the Java 2 permissions it needs
(
-Djava.security.policy=JAASProvider.policy)
-
Gives the client the JAAS permissions it needs
(
-Djava.security.auth.policy=SimpleJAAS.policy)
-
Tells the LoginContext where to find the configuration file
(
-Djava.security.auth.login.config=simple.conf)
You should see a session like the following (assuming you enter the correct answer to the question):
What can Jack Burton see?
things no one else can see
Login assigned these principals:
SimplePrincipal: Jack
You live at {your home directory}
|
In general, you do not need to write your own providers or Principals. Instead, you can use a standard provider and Principals that integrate with your OS login, smart card, or other authentication provider. Otherwise, what you do to build and run an application that uses JAAS is very similar to what was shown above. Specifically, you need to:
-
Create a
LoginContext that refers to a named JAAS policy entry.
-
Configure your provider in the Java 2 security policy.
-
Configure your JAAS policy.
-
Use
Subject.doAs to control sensitive operations.
The example code shown above is quite simple. Here are some other experiments you could try:
-
A real authentication provider should not use
System.in and
System.err. The CallbackHandler interface is provided for this
purpose. Rewrite the SimpleLoginModule to use a
CallbackHandler provided by the client.
-
Providers do not actually need
AllPermission. To discover the
permissions that are really needed, remove the AllPermission
entries from the JAASProvider.policy file. Then, run the
application with the -Djava.security.debug=access,failure flag
to dump the name of permission checks that are failing. When
you see a permission fail, add it back to the security file and
try again. Warning: This will take a while! But, if you
investigate each permission as you go, you will gain an
intimate understanding of how JAAS works.
To learn more about JAAS 1.0, see the Java Authentication and Authorization Service (JAAS) 1.0 page. Scroll down on that page to the "More Info" section for several useful links.
The 1.4 version of Java 2 Platform, Standard Edition incorporates JAAS. A beta version of J2SE version 1.4 is available for download.
- NOTE
Sun respects your online time and privacy. The Java Developer Connection mailing lists are used for internal Sun Microsystems purposes only. You have received this email because you elected to subscribe. To unsubscribe, go to the Subscriptions page, uncheck the appropriate checkbox, and click the Update button.
As of May 22, 2001, Sun Microsystems updated its Privacy Policy to give you a better understanding of Sun's Privacy Policy and Practice. If you have any questions, contact privacy@sun.com.
- SUBSCRIBE
To subscribe to a JDC newsletter mailing list, go to the Subscriptions page, choose the newsletters you want to subscribe to, and click Update.
- FEEDBACK
Comments? Send your feedback on the JDC Tech Tips to:
jdc-webmaster@sun.com
- ARCHIVES
You'll find the JDC Tech Tips archives at: http://java.sun.com/jdc/TechTips/index.html
- COPYRIGHT
Copyright 2001 Sun Microsystems, Inc. All rights reserved. 901 San Antonio Road, Palo Alto, California 94303 USA.
This document is protected by copyright. For more information, see:
http://java.sun.com/jdc/copyright.html
- LINKS TO NON-SUN SITES
The JDC Tech Tips may provide, or third parties may provide,
links to other Internet sites or resources. Because Sun has no
control over such sites and resources, You acknowledge and agree
that Sun is not responsible for the availability of such external
sites or resources, and does not endorse and is not responsible
or liable for any Content, advertising, products, or other
materials on or available from such sites or resources. Sun will
not be responsible or liable, directly or indirectly, for any
damage or loss caused or alleged to be caused by or in connection
with use of or reliance on any such Content, goods or services
available on or through any such site or resource.
JDC Tech Tips
July 27, 2001
Sun, Sun Microsystems, Java, Java Developer Connection, and J2SE
are trademarks or registered trademarks of Sun Microsystems, Inc.
in the United States and other countries.
|