Dwarf Server Framework 1.3.1

Security Guide


Content

 

Introduction

This document tries to cover all aspects of the security in Dwarf-based applications. It gives a detailed information on configuring and using the security-related features.


Security overview

Dwarf framework uses the Java™ 2 Paltform Security Architecture (standard security) together with the subject-based security provided by the javax.security.auth package.

The standard security is used to protect the application as well as the hosting platform in a way that the specific permissions are granted to the particular code source. In other words, we tell the Java Runtime Environment which permissions are granted to the code obtained from a particular URL. A typical standard security policy entry may look like this:

grant codeBase "file:/home/user/java/*" 
{ permission java.io.FilePermission ".";
}; 

This example grants the given java.io.FilePermission "." to a code obtained from the "/home/user/java" directory. The codeBase parameter thus represents the source of the code.

The subject-based security adds another level to this: we define also which subjects are permitted to run a code from the given source. A subject can represent an user or any other identity (software service, etc.), which runs the given code. A typical subject-based policy entry may look like this:

grant codeBase "file:/home/user/java/*", 
principal SK.gnome.dwarf.auth.UserPrincipal "admin"
{ permission java.io.FilePermission ".";
}; 

The example above grants the java.io.FilePermission "." to a code obtained from the "/home/user/java" directory, if it is run by the given subject. Subjects typically contain one or more principals - instances of the java.security.Principal class. These principal objects represent various identities of the authenticated subject.

Since the Dwarf framework is based on services as its lowest-level functional units, it uses a special case of the subject-based policy. It defines that a permission can be granted to both the user and the service. We could say that a service is running the particular code on behalf of the given user. Let's see an example:

grant principal SK.gnome.dwarf.auth.ServicePrincipal "Main Server/Console",
principal SK.gnome.dwarf.auth.UserPrincipal "admin"
{ SK.gnome.dwarf.auth.ServicePrincipal "executeCommand.*";
}; 

This policy entry grants a permission to execute any console command by the service "Main Server/Console" to the authenticated user "admin". The user must be represented by the UserPrincipal object, and the service by the ServicePrincipal object. The actual service may be of course any service with the given full name, but only instances of the Console class do check this special executeCommand permission, so for now it would be useless to grant it to any other one.

Note: although the codeBase parameter can be also specified in the subject-based policy entry, we omit it here for the sake of the simplicity. In this case the policy entry will apply to any code source.

Designed this way, the Dwarf framework provides several important features:


Basic security

In this chapter we will learn how to turn on the Java security manager, how to install a standard security policy and how to install a subject-based security policy.


Security manager

The Dwarf security features works only if the Java security manager is installed. By default an instance of the java.lang.SecurityManager is used. You can install the security manager in several ways. The preferred way is via the XML-based configuration file. Use the security attribute of the root configuration element and set it to "on". This will automatically activate the security manager before the application is initialized:

<configuration security="on" debug="tcp" trace="" verbose="off">
  ...
</configuration>

(See the XML Configuration Guide for more information about the XML-based configuration.)

Another way is to set the java.security.manager system property via the command line when executing the application:

java -Djava.security.manager ... SK.gnome.dwarf.main.Main

It is also possible to install the security manager programatically in your own code, but this is beyond the scope of this document.


Standard security policy

If we are going to install the security manager, we should also tell the Java Runtime Environment where to find the security policy files, which will be read and parsed when initializing the security subsystem. The preferred way is to set the "java.security.policy" system property via the XML-based configuration:

<configuration security="on" debug="tcp" trace="" verbose="off">

  <!-- STANDARD SECURITY POLICY -->
  <property name="java.security.policy">file:conf/java.policy</property>
  ...
</configuration>

The property value should be an URL of the target policy file. For example, in the sample Dwarf configuration files it is the "conf/samples/dwarf/java.policy" file. Please look at the content of this file - you can see that a very relaxed security policy is choosen for the sample applications. The AllPermission instance here always implies any other permission. If you intend to run the application in a real sandbox, you should use more restricted policy instead. An example of such a restricted policy is in the "conf/samples/dwarf/java_restricted.policy" file.

Please note also that just two code sources are specified in the sample policy file: "lib/ext/*" and "lib/*". The first one is used for the required Java extensions and the second one for the Dwarf class libraries as well as your custom class libraries. Note also that no code from the "lib/priv" subdirectory has been granted any permissions. This is very important, because it contains only one JAR file with a single CheckPermissionAction class in it. This class is used as a stub for checking the subject-based permissions, therefore no permissions should be granted to it explicitly. If this rule is violated, the subject-based authorization will not work as expected!

Standard security policy can be also installed by setting the system property via the command line:

java -Djava.security.manager -Djava.security.policy=file:conf/java.policy ... SK.gnome.dwarf.main.Main


Subject-based security policy

A subject-based security policy can be installed in a similar way - by settting the "java.security.auth.policy" system property in the XML-based configuration file:

<configuration security="on" debug="tcp" trace="" verbose="off">

  <property name="java.security.policy">file:conf/java.policy</property>

  <!-- SUBJECT-BASED SECURITY POLICY -->
  <property name="java.security.auth.policy">file:conf/jaas.policy</property>
  ...
</configuration>

The property value should be an URL of the target subject-based policy file. In the sample Dwarf configurations it is the "conf/samples/dwarf/jaas.policy" file. Please look at the content of this file. The default sample policy grants only one permission to execute all console commands by the "Main Server/Console" service to all local users.

Subject-based security policy can be also installed via the command line:

java -Djava.security.manager -Djava.security.policy=file:conf/java.policy \
-Djava.security.auth.policy=file:conf/jaas.policy ... SK.gnome.dwarf.main.Main


Additional security policies

Additional policies can be installed via setting the corresponding security properties "policy.url.n" and "auth.policy.url.n". It is more flexible way because you don't have to edit the existing policy files. Instead of it you just create new files with additional permissions required by your services and include them via the XML-based configuration. Please refer to the XML Configuration Guide for more information on setting the security properties.


Authentication

Authentication in Dwarf is a process of identifying subjects which run the application services. Each service inherits a couple of methods which can be used for the authentication purposes. However, these methods provide just an abstract authentication layer. Therefore, Dwarf provides also an implementation of a real authentication service based on the javax.security.auth.login package, together with several application-independent login modules.


Invoking the authentication methods

The authentication methods used by the Dwarf are declared in the Service interface:

  public AuthId login(Subject subject, CallbackHandler handler) throws LoginException;


  public void logout(AuthId id, Subject subject) throws LoginException;

These methods can be used to login and logout the given subject. Since each instance of the Subject can be in general authenticated more than once, the AuthId object is used to relate the login/logout operations. The handler parameter provides a channel for exchanging the authentication information (usernames, paswords, etc.) between the subject and the underlying login mechanism.

However, these methods do not imply any concrete login mechanism. The only available default implementation of the Service interface - the GenericService class - implements these methods in a way that they are in effect (actually via the getAuthenticator() method) propagated to the top-level main server. Thus if the service is not contained by a server, an exception is thrown to indicate that no authentication mechanism is currently available. A top-level server should finally provide a service which can do the real authentication job. This is what we stated previously that the authentication methods form just an abstract layer and they do not imply any particular authentication facility. (Later we explain how to specify and use a real authentication service in the Dwarf environment.)

Let's see the sample usage of the authentication methods:

 // instantiate a new empty subject
 Subject subject = new Subject();


 // login the subject
 AuthId id = login(subject, new BasicCallbackHandler("joe", "public"));

 // ... perform any action in behalf of the authenticated user "joe"

 // logout the subject
 logout(id, subject);  

This example tries to authenticate an user with name "joe" and password "public" by using a default authentication facility. If the authentication is successful, the Subject instance will contain at least one java.security.Principal instance, identifying the user. (Note at this moment that we still do not know anything about the underlying authentication engine and simply pretend that it is somehow configured and working properly.)
Having the subject authenticated, any subject-based permission can be checked if it is granted to the given user, but this will be discussed later. Finally, if the authenticated subject is not needed any more, it can be logout via the logout method. The principals added by the related login operation should be removed from the subject by this logout process.

The login method inherited from the GenericService object automatically adds an instance of the ServicePrincipal class to the subject beeing authenticated. This principal will be constructed with the full name of the service which initiated the authentication, thus unambigiously referring to it. The logout method in turn removes this principal from the subject. This is because a subject is authenticated in the context of the service which invoked the authentication process. It allows to grant the subject-based permissions to both the user and the service.


Configuring the authentication services

We know what methods to use for the login and logout operations. But we know also that by default the authentication requests end up in the top-level server. So the question is where and how the real authentication work is performed?

The answer is in the Authenticator interface. Its purpose is to implement services which are the real authenticators, i.e. they can perform the actual login and logout operations. If a service implementing this interface is added to an instance of the GenericServer class, the server object will use it to perform the authentication operations. Such an Authenticator must therefore implement its login and logout methods to perform the real authentication, of course.

And that's it...! Create a service implementing the Authenticator interface and add it to a server - it will be used automatically for the login and logout operations by the whole service subtree. This allows to have either just one shared authentication service plugged-in to the main server object, or to have a several independent authentication services for each particular application subsystem.

The base framework provides just one implementation of this interface - the JAASAuthenticator class. It authenticates the subjects using the login module-based mechanism supported by the javax.security.auth.login package.

So finally, we have discovered where an how the authentication is incoporated into the framework. Because a custom authenticators (which even need not to use the javax.security.auth.login package at all) may be developed and used, as well, the authentication design is transparent and very open to the different implementation approaches.


Installing the login configuration

Let's assume now that we have configured the JAASAuthenticator instance and are going to use it for the authentication in a Dwarf-based application. Because the used login mechanism depends on the configuration of login modules, we need to know how to setup such a login configuration.

The desired login configuration can be installed by settting the "java.security.auth.login.config" system property via the XML-based configuration file:

<configuration security="on" debug="tcp" trace="" verbose="off">

  <property name="java.security.policy">file:conf/java.policy</property>
  <property name="java.security.auth.policy">file:conf/jaas.policy</property>

  <!-- LOGIN CONFIGURATION -->
  <property name="java.security.auth.login.config">file:conf/jaas.config</property>
  ...
</configuration>

The property value should be an URL of the target login configuration. In the sample Dwarf configuration it is the "conf/samples/dwarf/jaas.config" file. Please look at the content of this file. The default sample configuration defines only one login configuration named "Dwarf" with only one required login module - the PlaintextLoginModule. Location of the user database for this module is specified via the "url" parameter.

Custom login configuration can be also installed via the command line:

java -Djava.security.manager -Djava.security.policy=file:conf/java.policy \
-Djava.security.auth.policy=file:conf/jaas.policy \ -Djava.security.auth.login.config=file:conf/jaas.config ... SK.gnome.dwarf.main.Main


Installing the additional login configurations

Additional login configurations can be installed by setting the "login.config.url.n" security property to a desired URL. It is more flexible way because you don't have to edit the existing login configuration file. Instead of it you just create new files with the additional sets of login modules and include them to the XML-based configuration file. Please refer to XML Configuration Guide for more information on setting the security properties.


Choosing the authentication facility

The service can choose which login configuration it will use in the authentication by consulting its getAuthFacility() method. The returned value is inherited from the parent server by default, but can be changed also via the service's setAuthFacility(String) method. The simplest usage is thus to set this attribute for the top-level server, typically an instance of the Main Server. All directly or indirectly nested services will then share this authentication facility by default.

The sample XML configuration entry for setting the authentication facility may look like this:

<service class="SK.gnome.dwarf.main.MainServer" name="Main Server">
  <set name="logFacility">server</set>
  <set name="authFacility">Dwarf</set>

  <!-- ... other services ... -->

</service>


Login modules

Dwarf distribution provides generic classes for login modules based on the javax.security.auth.login package, which can be found in the SK.gnome.dwarf.auth.login package. The GenericLoginModule can be extended easily to implement a custom login module. This package contains also some basic ready-to-use login module implementations.


Authorization

Authorization in Dwarf is a process of checking whether the given authenticated subject has been granted a permission to do the particular action. Such permissions are also called a subject-based permissions, beacuse they specify not only the possible source of code, but also who may run the code. In Dwarf, the code is run by a service on behalf of the user. Therefore permissions must be granted to both the user and the service.


Configuring the subject-based permissions

Subject-based permissions are configured by installing a subject-based security policy. See the Installing the subject-based security policy section for more information.

Dwarf framework uses a special case of the subject-based policy. It requires that a permission is granted to both the user and the service. Let's see a real example:

  grant principal SK.gnome.dwarf.auth.ServicePrincipal "Main Server/Console",
  principal SK.gnome.dwarf.auth.RolePrincipal "administrators"
  { permission SK.gnome.dwarf.auth.ServicePermission "executeCommand.*";
  }

If a service whose full name is "Main Server/Console" invokes authentication of an user, and the successful authentication process adds an instance of the RolePrincipal class with the name "administrators" to the authenticated subject, then the given permission is granted to the subject and the service.

Such a subject-based policy entry may contain more than two principal objects, but remember that if you specify more principals in one policy entry, all these principals must be contained in the checked subject, otherwise the subject would not be authorized.

Note: Each subject-based permission used in the subject-based policy must be specified also in the standard security policy. This is forced by the JDK implementation. Thus if you want to grant a subject-based permission, you should generally perform two steps:

The wildcard character can be used effectively in the second step. If you know that you are going to use several ServicePermission "executeCommand.xxxxxx" permissions, it might be better to add the following entry to the standard security policy instead of the previous one:

grant codeBase "file:lib/*" 
{ // ...

  // custom permission
  permission SK.gnome.dwarf.auth.ServicePermission "executeCommand.*";
}; 


Checking the subject-based permissions

Finally, we need to know how to check whether a subject has been granted the given permission. We use the CheckPermissionAction utility class to do this. An instance of this class must be passed in to the javax.security.auth.Subject.doAs static method along with the currently authenticated subject. This should end-up in throwing an java.security.AccessControlException if the desired permission is not implied by the actual subject-based policy:

  Permission permission = new SamplePermission("perm1");
  try
  { Subject.doAs(subject, new CheckPermissionAction(permission));
    System.err.println("Permission is granted");
  }
  catch (AccessControlException e)
  { System.err.println("Permission is not granted: " + e.getMessage());
  }

(Please remember that no standard permissions may be granted to the code source of the CheckPermissionAction class. If you break this rule, the subject-based authorization will not work as expected!)

Look at the Command Console Tutorial also for a real example of the authentication and authorization code.


Using SSL/TLS

You may configure your TCP/IP server to communicate over a SSL/TLS encrypted connection. To test the connection, you must create a server certificate first. Change the current working directory to "bin/" subdirectory of the Dwarf installation home and edit the "keystore.bat" or "keystore.sh" script to match your needs. Run the script to create a test key store with sample server certificate, located in "conf/testkey.crt". Then configure the TCPIPServer instance and add the following required attributes to it, as well as a SSLListener:

  <!-- TCP/IP SERVER -->

  <service classbase="SK.gnome.dwarf.tcpip" class=".TCPIPServer" name="TCP/IP Server">
    <set name="logFacility">tcpip</set>

    <set name="keyStoreURL">file:conf/testkey.crt</set>
    <set name="keyStorePwd">123456</set>
    <set name="keyPwd">abcdef</set>
    <set name="SSLProtocol">TLSv1</set>

    <!-- ... -->

    <!-- STANDARD LISTENER ON PORT 8000 -->

    <service class="SK.gnome.dwarf.tcpip.TCPListener" name="Listener">
      <set name="port">8000</set>
    </service>

    <!-- SSL/TLS LISTENER ON PORT 4430 -->

    <service class="SK.gnome.dwarf.tcpip.SSLListener" name="SSL/TLS Listener">
      <set name="port">4430</set>
    </service>

  </service>

</configuration>

The SSL/TLS listener will listen on the specified port and pass the accepted connections to the server in the same way as the normal one.


Return to the main page.


Copyright (c) 1999-2005, Gnome Ltd. All rights reserved.