Understanding Java Security and JAAS (part 2)

2011-05-12

This is the second article in the three-part Java Security and JAAS series. The first part explains the basics of Java Security, this (second) part explains the concept and use of JAAS. There is a demo associated with this part as well (see link a the end of the article). In the last part we will be exploring writing your own JAAS LoginModule to integrate Java with a security domain.

In the first part we saw that Java Security is based on explicitly granting permissions to a code source. This code source is made up of the location (URL) from where the class resides, optionally in combination with a signature of the "owner" of the code (using signer jar files). JAAS (Java Authentication and Authorization Services) adds a third dimension: the user executing the code.

Authentication

JAAS adds, "who is executing" to "where is the code residing" and "who is the owner of the code"

In order to establish "who" we need to perform authentication (proof of identity). After we have established we can use this intelligence to authorise the user. This authorisation extends the grant principle you have seen in the first part.

JAAS authenticates a Subject, this is the person/user/etc that has to be authenticated. The authentication process is managed by the LoginContext. This class is also your entry point when using JAAS. A LoginModule knows how to perform authentication for a particular security domain (such as LDAP, a database, Unix security, Active Directory etc). The LoginContext uses a named configuration which specifies what LoginModules must be processed in order to authenticate the subject. The LoginContext is usually configured to perform with a single LoginModule, but you can stack them as well. For example the subject must be authenticated in both LDAP and in our user database (as depicted in the diagram below).

jaas overview

Note when stacking LoginModules, you can specify the importance of such a module within the context of the stack. In the above example. LDAP is "required" and the Database is flagged "sufficient". The list below explains the semantics of each flag (you might notice the above combination makes no sense!)

  • required - The module must succeed; Login process will always continue
  • requisite - The module must succeed otherwise login process fails
  • Sufficient - When module succeeds, login process is completed
  • Optional - Login process will continue regardless of the outcome

The configuration of the LoginModules to use resides in a JAAS configuration file. Below is an example:

SomeApp  {
   com.sun.security.auth.module.NTLoginModule REQUIRED;
};

SomeOtherApp{
  com.sun.security.auth.module.LdapLoginModule REQUIRED
      userProvider="ldap://our-ldap-server/ou=people,dc=highview,dc=com"
      ...

  demo.our.own.DatabaseLoginModule SUFFICIENT
      datasource="userDS"
      table="Members"
};

This JAAS configuration files defines two configurations: "SomeApp" and "SomeOtherApp". The first defines a single LoginModule, the NTLoginModule (one of the bundled modules with Oracle's JVM). The second example shows a stack of two modules, of which one is a custom LoginModule

Recall the LoginContext is the entry point when wanting to authenticate a subject. You pass the name of the configuration when you instantiate the LoginContext (in our example "SomeApp" or "SomeOtherApp" ):

LoginContext loginContext= new LoginContext("SomeApp");
try{
  loginContext.login();
  System.out.println("You are real!")
  Subject subject = loginContext.getSubject();
  ...
} catch (LoginException e){
  System.err.append("Authentication failed: ").println(e.getMessage());
}

When the LoginContext traverses over its configured LoginModules, it calls various login-lifecycle methods (login, commit, etc). Each module during the login-process performs the authentication or more likely delegates this to a security domain. In any case from a JAAS perspective it is the LoginModule's responsibility to authenticate the subject. When a subject is authenticated, it gets assigned one or more Principals. A Principal is a manifestation of an authenticated user. This could be group, known user, NT SID, etc. Below is a list of principals assigned when using the NTLoginModule and UnixLoginModule (this is available in the demo for Windows, Linux and Mac)

UnixPrincipal: jwirth
UnixNumericUserPrincipal: 1000
UnixNumericGroupPrincipal [Primary Group]: 100
UnixNumericGroupPrincipal [Supplementary Group]: 16
UnixNumericGroupPrincipal [Supplementary Group]: 17
UnixNumericGroupPrincipal [Supplementary Group]: 20
...
// Windows
NTUserPrincipal: jwirth
NTSidUserPrincipal: S-1-5-21-73586283-1677128483-839522115-1003
NTDomainPrincipal: JWWIN
NTSidDomainPrincipal: S-1-5-21-73586283-1677128483-839522115
NTSidPrimaryGroupPrincipal: S-1-5-21-73586283-1677128483-839522115-513
NTSidGroupPrincipal: S-1-1-0
NTSidGroupPrincipal: S-1-5-32-544

These Principal objects are assigned to the Subject by the LoginModule (during the commit method). These principals are important (in fact they are the "goal" in Java authentication) as they are used for authorisation.

Take a look at the diagram below as a summary of what we talked about so far (with the exception of CallbackHandlers)

JAAS design

A CallbackHandler allows an application to interact with the login process if needed. When creating the LoginContext you can pass an instance supporting this interface. For example you might want to create your own username/password dialog in the look&feel of you application. In the next post I will show you how to use this callback handler ##Configuring Authorisation When you want to grant a Java Permission based on the user's identity, you use the principals it gets assigned to after he/she has been authenticated by the LoginModules(s) This is sometimes referred to as "Principal Authentication". (however that term is more used in Java EE authentication mechanisms using deployment descriptors)

Here is an example granting permissions to a principal (from the attached demo):

// change "jwirth" with your username
// for windows use: com.sun.security.auth.NTUserPrincipal
grant
  principal com.sun.security.auth.UnixPrincipal "jwirth"
  codeBase "file:./classes/-" {
    permission java.io.FilePermission "etc/*", "read, write, delete, execute";
};

Important note. This JVM instance will obviously run with a security manager enabled. Using JAAS falls under security restrictions (for one loading JAAS configuration file for example). You need to therefore grant permissions to the code using the LoginContext (don't mind the doAsPrivileged permission, that will become clear soon!)

grant codeBase "file:./classes/-" {
  permission javax.security.auth.AuthPermission "createLoginContext.*";
  permission javax.security.auth.AuthPermission "doAsPrivileged";
};

The * behind the createLoginContext means the classes are allowed to create a LoginContext for any configuration, you could make it more specific and specify the name of the configuration: createLoginContext.os-demo-unix

Authorisation at run-time

The big question now is how is the occurrence of a Principal within the Subject taken into consideration when checking security permissions at run-time?

Recall that when a class is loaded a CodeSource is attached to it (thought the ProtectDomain, which is interrogated for a permission using its implies method by the AccessControlContext). With JAAS this ProtectionDomain also gets a reference to a list of Principals.

class diagram

Now the implies calls on the ProtectionDomain can take the Principals in consideration when being asked if it implies a particular security permission. Then the big questions becomes:" how are the principals attached to the ProtectionDomain" (they reside inside the Subject, and are now known when the class is loaded!). The answer is you attach them by running a "closure" within the context of the Subject. The following code will hopefully start to make this clearer:

Subject.doAsPrivileged(subject, new PrivilegedExceptionAction<Object>() {
      public Object run() throws Exception {
        FileReader fr = new FileReader(file);
        try {
          System.out.println("Opened the file!");
        finally {
          fr.close();
        }
        return null;
      }
    }}, null);

What's happening here is that a the code in the run method of the PrivilegedExceptionAction runs within the context of the Subject. This means the ProtectionDomains of the classes get assigned the principals of the subject (notice the doAsPrivileged is a method of the Subject class ). It is very similar to the PrivilegedAction.doPrivileged method you have seen in the previous post. Note there is a PrivilegedAction class as well that does not allow an exception to escape your run method. The last argument of the doAsPrivileged is the AccessConrolContext which for Java SE applications is normally null. When null a new AccessControlContext is created for this privileged action (would be a great candidate for currying this method)

Please find the accompanying demo here (you need to edit the etc/demo.policy for your environment, take a look at the comments in this file)

Part 3, can be found here

This article does not necessarily reflect the technical opinion of EDC4IT, but purely of the writer. If you want to discuss about this content, please use thecontact ussection of the site