Cloudscape and Security
Page 3 of 10

Working with User Authentication

Overview

Cloudscape provides support for user authentication. User authentication means that Cloudscape authenticates a user's name and password before allowing that user access to the system.

When user authentication is enabled (which it is not by default), the user requesting a connection must provide a valid name and password, which Cloudscape verifies against the repository of users defined for the system. Once Cloudscape authenticates the user, it grants the user access to the Cloudscape system but not necessarily access to the database made in the connection request. In the Cloudscape system, access to a database is determined by user authorization. For information, see User Authorization.

Cloudscape allows you to provide a repository of users in a number of different ways. For example, you can hook Cloudscape up to an external directory service elsewhere in your enterprise, create your own, use Cloudscape's simple mechanism for creating a built-in repository of users.

You can define a repository of users for a particular database or for an entire system, depending on whether you use system-wide or database-wide properties. See Configuring Security for Your Environment for more information.

When Cloudscape user authentication is enabled and Cloudscape uses an external directory service, the architecture looks something like that shown in Figure 8-3.

Figure 8-3 Cloudscape user authentication using an external service. The application can be a single-user application with an embedded Cloudscape engine or a multi-user application server.

Cloudscape always runs embedded in another Java application, whether that application is a single-user application or a multiple-user application server or connectivity framework. A database can be accessed by only one application at a time, so it is possible to deploy a system in which the application in which Cloudscape is embedded, not Cloudscape, handles the user authentication by connecting to an external directory service. For example, Cloudconnector can provide user authentication. In that scenario, Cloudconnector would authenticate the user name and password before granting access to the system in question. It would then grant the user access to the Cloudscape system and would pass the user's name to Cloudscape.

Figure 8-4 The application provides the user authentication using an external service. The application can be a single-user application with an embedded Cloudscape engine or a multi-user application server.

Enabling User Authentication

To enable user authentication, set the cloudscape.connection.requireAuthentication property to true. Otherwise, Cloudscape does not require a user name and password. You can set this property as a system-wide property or as a database-wide property.

For a multi-user product, you would typically set it for the system in the cloudscape.properties file for your server, since it is in a trusted environment.

NOTE: If you start a Cloudscape system with user authentication enabled but without defining at least one user, you will not be able to shut down the system gracefully. When Cloudscape is running in a connectivity server and user authentication is turned on, stopping the server requires a user name and password. You will need to alter shutdown scripts accordingly. See the Cloudscape Server and Administration Guide for information.

Defining Users

Cloudscape provides several ways to define the repository of users and passwords. To specify which of these services to use with your Cloudscape system, set the property cloudscape.authentication.provider to the appropriate value as discussed in the appropriate section listed below.

Setting the property as a system-wide property creates system-wide users. Setting the property as a database-wide property creates users for a single database only.

NOTE: Shutting down the Cloudscape system (for example, using the shutdown=true form of the database connection URL without specifying a particular database) when user authentication is turned on requires that you define at least one user as a system-wide user.

External Directory Service

A directory service stores names and attributes of those names. A typical use for a directory service is to store user names and passwords for a computer system. Cloudscape uses the Java naming and directory interface (JNDI) to interact with external directory services that can provide authentication of users' names and passwords.

In Version 3.0, Cloudscape can use the following services:

LDAP Directory Service

You can allow Cloudscape to authenticate users against an existing LDAP directory service within your enterprise. LDAP (lightweight directory access protocol), an emerging Internet standard, provides an open directory access protocol running over TCP/IP. An LDAP directory service can quickly authenticate a user's name and password.

To use an LDAP directory service, set cloudscape.authentication.provider to LDAP.

Examples of LDAP service providers are:

  • Netscape Directory Server

    Netscape Directory Server is an LDAP directory server. In addition, the Netscape Directory Synchronization Service synchronizes entries in a Windows NT directory with the entries in Netscape's Directory Server. It allows you to use the Windows NT directory as a repository for Cloudscape users.

  • UMich slapd (freeware for the UNIX platform from the University of Michigan)
  • AE SLAPD for Windows NT, from AEInc

Cloudscape 2.0 has been tested with Netscape Directory Server and Netscape Directory Synchronization Service.

Libraries for LDAP User Authentication

To use an LDAP directory service with Cloudscape, you need the following libraries in your class path:

  • jndi.jar

    JNDI classes

  • ldap.jar

    LDAP provider from Sun

  • providerutil.jar

    JNDI classes for a provider

Cloudscape does not provide these libraries; they are available from JavaSoft on the JNDI page at http://java.sun.com:80/products/jndi/. Use the 1.1.x versions of these libraries, not the 1.2.x versions. You may need to do two separate downloads to obtain all the required libraries.

Setting Up Cloudscape to Use Your LDAP Directory Service

When specifying LDAP as your authentication service, you must specify the location of the server and its port number.

cloudscape.authentication.server=godfrey:389

Guest Access to Search for DNs

In an LDAP system, users are hierarchically organized in the directory as a set of entries. An entry is a set of name-attribute pairs identified by a unique name, called a DN (distinguished name). An entry is unambiguously identified by a DN, which is the concatenation of selected attributes from each entry in the tree along a path leading from the root down to the named entry, ordered from right to left. For example, a DN for a user might look like this:

cn=mary,ou=People,o=cloudscape.com
uid=mary,ou=People,o=cloudscape.com

The allowable entries for the name are defined by the entry's objectClass.

An LDAP client can bind to the directory (successfully log in) if it provides a user ID and password. The user ID must be a DN, the fully qualified list of names and attributes. This means that the user must provide a very long name.

Typically, the user knows only a simple user name (e.g., the first part of the DN above, WilliamS). With Cloudscape, you do not need the full DN, because an LDAP client (Cloudscape) can go to the directory first as a guest or even an anonymous user, search for the full DN, then rebind to the directory using the full DN (and thus authenticate the user).

Cloudscape typically initiates a search for a full DN before binding to the directory using the full DN for user authentication. Cloudscape does not initiate a search in the following cases:

For more information, see cloudscape.authentication.ldap.searchFilter in Tuning Cloudscape.

Some systems permit anonymous searches; other require a user DN and password. You can specify a user's DN and password for the search with the properties listed below. In addition, you can limit the scope of the search by specifying a filter (definition of the object class for the user) and a base (directory from which to begin the search) with the properties listed below.

To narrow the search, you can specify a user's objectClass.

Performance Issues

For performance reasons, the LDAP directory server should be in the same LAN as Cloudscape. Cloudscape does not cache the user's credential information locally and thus must connect to the directory server every time a user connects.

Connection requests that provide the full DN are faster than those that must search for the full DN.

Windows NT Users

Netscape provides LDAP functionality for Windows NT systems with its Netscape Directory Synchronization service, which synchronizes the Windows NT users with the Netscape Directory Server. SSL is recommended in this configuration.

Restrictions

Cloudscape Version 3.6 does not support LDAP groups.

NIS Directory Service Plus (NIS+)

Network Information Services Plus (NIS+) is an enhanced version of the NIS name service used on the Sun Solaris and SunOS operating systems. In this system, users are contained in the NIS+ table called passwd.org_dir and groups in the table called group.org_dir.

To use an NIS+ directory service, set cloudscape.authentication.provider to NIS+.

Libraries for NIS Directory Service Plus User Authentication

To use NIS+ directory service with Cloudscape, you need the following libraries in your class path:

  • jndi.jar

    JNDI classes

  • nisplus.jar

    NIS+ provider from Sun, Early Release II

  • providerutil.jar

    JNDI classes for a provider

Cloudscape does not provide these libraries; they are available from JavaSoft on the JNDI page at http://java.sun.com:80/products/jndi/. Use the 1.1.x versions of these libraries, not the 1.2.x versions. You may need to do two separate downloads to obtain all the required libraries.

Configuring Cloudscape for NIS+ User Authentication

You must set the cloudscape.authentication.server property to

//nis.server.name/nis.domain

For example:

//flare/cloudscape.com

JNDI-Specific Properties for External Directory Services

Cloudscape allows you to set a few advanced JNDI properties, which you can set in any of the supported ways of setting Cloudscape properties. Typically you would set these at the same level (database or system) for which you configured the external authentication service.

The list of supported properties can be found in Appendix A: JNDI Context Environment in the Java Naming and Direction API at http://java.sun.com:80/products/jndi/docs.html. The external directory service must support the property.

Each JNDI provider has its set of properties that you can set within the Cloudscape system.

For example, you can set the property java.naming.security.authentication to allow user credentials to be encrypted on the network if the provider supports it. You can also specify that SSL be used with LDAP (LDAPS).

User-Defined Class

Set cloudscape.authentication.provider to the full name of a class that implements the public interface COM.cloudscape.authentication.Interface.AuthenticationScheme.

By writing your own class that fulfills some minimal requirements, you can hook Cloudscape up to an external authentication service other than LDAP or NIS+. To do so, specify an external authentication service by setting the property cloudscape.authentication.provider to a class name that you want Cloudscape to load at startup.

The class that provides the external authentication service must implement the public interface COM.cloudscape.authentication.Interface.AuthenticationScheme and throw exceptions of the type COM.cloudscape.authentication.Interface.AuthenticationException where appropriate.

Using a user-defined class makes Cloudscape adaptable to various naming and directory services.

A very simple example of a class that implements the interface follows.

import COM.cloudscape.authentication.Interface.*;
import java.io.FileInputStream;
import java.util.Properties;
/**
  * A simple example of a specialized Authentication scheme.
  * The system property 'cloudscape.connection.requireAuthentication
  * must be set
  * to true and 'cloudscape.connection.specificAuthentication' must
  * contain the full class name of the overriden authentication
  * scheme,  i.e., the name of this class.
  *
  * @see COM.cloudscape.authentication.Interface.AuthenticationScheme 
  */

public class MyAuthenticationSchemeImpl implements
AuthenticationScheme {
    private static final String USERS_CONFIG_FILE = "myUsers.cfg";
    private static Properties usersConfig;

    // Constructor
    // We get passed some Users properties if the 
    //authentication service could not set them as 
    //part of System properties.
    //
    public MyAuthenticationSchemeImpl() {
    }
    /* static block where we load the users definition from a
users configuration file.*/
    static {
       /* load users config file as java properties
        File must be in the same directory where
        Cloudscape gets started.
       (otherwise full path must be specified) */
       FileInputStream in = null;
       usersConfig = new Properties();
       try {
           in = new FileInputStream(USERS_CONFIG_FILE);
           usersConfig.load(in);
           in.close();
       } catch (java.io.IOException ie) {
           // No Config file. Raise error message
           System.err.println(
             "WARNING: Error during Users Config file
retrieval");
           System.err.println("Exception: " + ie);
       }
    }
    /**
     * Authenticate the passed-in user's credentials.
     * A more complex class could make calls
     * to any external users directory.
     * NOTE: AuthenticationException must be thrown upon failure.
     *
     * @param userName               The user's name
     * @param userPassword           The user's password 
     * @param databaseName           The database 
     * @param infoAdditional jdbc connection info.
     * @exception AuthenticationException on failure
     */
    public voidauthenticateUser(String userName,
     String userPassword,
     String databaseName,
     Properties info
        )
       throws AuthenticationException 
    {
       /* Specific Authentication scheme logic.
        If user has been authenticated, then simply return.
        If user name and/or password are invalid, 
        then raise the appropriate exception:
        new AuthenticationException.loginFailed(); 
        If the user is not a valid user of the database,
        then raise:
        new AuthenticationException.notAValidUserInDatabase
             (userName, databaseName);
       For other (exceptional) exception, raise:
       new AuthenticationException.loginFailed(myExceptionMsg); 
       This example allows only users defined in the
       users config properties object.

       Check if the passed-in user has been defined for the system.
       We expect to find and match the property corresponding to
       the credentials passed in. */
       if (userName == null)
           // We don't tolerate 'guest' user for now.
           throw AuthenticationException.loginFailed();
       //
       // Check if user exists in our users config (file)
       // properties set.
       // If we didn't find the user in the users config set, then
       // try to find if the user is defined as a System property.
       //
       String actualUserPassword;
       actualUserPassword = usersConfig.getProperty(userName);
       if (actualUserPassword == null)
           actualUserPassword = System.getProperty(userName);
       if (actualUserPassword == null)
           // no such passed-in user found
           // Raise expected exception 
           throw AuthenticationException.loginFailed();
       // check if the password matches
       if (!actualUserPassword.equals(userPassword))
           // Raise expected exception 
           throw AuthenticationException.loginFailed();

// Now, let's check if the user is a valid user of the database
       if (databaseName != null)
       {
           /* if database users restriction lists present, then check 
            if there is one for this database and if so, 
            check if the user is a valid one of that database.
            For this example, the only user we authorize in database
            DarkSide is user 'DarthVader'. This is the only database
            users restriction list we have for this example.
            We authorize any valid (login) user to access the
            OTHER databases in the system.
            Note that database users ACLs could be set in the same
            properties file or a separate one and implemented as you
            wish. */
           //
           if (databaseName.equals("DarkSide")) {
              // check if user is a valid one.
              if (!userName.equals("DarthVader"))
                  // This user is not a valid one of the passed-in
                  // database name. We raise the appropriate
                  // exception expected by Cloudscape.
                  throw AuthenticationException.notAValidDatabaseUser(
    userName,
    databaseName);
           }
       }
           // The user is a valid one in this database
       return;
    }

    /**
     * Returns authentication scheme name string. 
     * Typically a string that succinctly states and identifies
     * the authentication scheme.
     * @return authentication scheme name string.
     **/
    public String toString() {
       return "my authentication scheme";
    }
}

Built-in Cloudscape Users

Cloudscape provides a simple, built-in respository of user names and passwords.

To use the built-in repository, set cloudscape.authentication.provider to CLOUDSCAPE. Using built-in users is an alternative to using an external directory service such as LDAP.

cloudscape.authentication.provider=CLOUDSCAPE

You can create user names and passwords for Cloudscape users by specifying them with the cloudscape.user.UserName property.

NOTE: These user names are case-sensitive for user authorization. User names are SQL92Identifiers. Delimited identifiers are allowed:

cloudscape.user."FRed"=java

For more information on user names and SQL92Identifiers, see Users and Authorization Identifiers.

NOTE: For passwords, it is a good idea not to use words that would be easily guessed, such as a login name or simple words or numbers. A password should be a mix of numbers and upper- and lowercase letters.

Database-Level Properties

When you create users with database-level properties, those users are available to the specified database only.

You set the property once for each user. To delete a user, set that user's password to null.

-- adding the user sa with password `cloud3x9'

CALL PropertyInfo.setDatabaseProperty(
    'cloudscape.user.sa', 'cloud3x9')

-- adding the user mary with password `little7xylamb'
CALL PropertyInfo.setDatabaseProperty(
    'cloudscape.user.mary', 'little7xylamb')

-- removing mary by setting password to null
CALL PropertyInfo.setDatabaseProperty(
    'cloudscape.user.mary', null)

System-Level Properties

When you create users with system-level properties, those users are available to all databases in the system.

You set the value of this system-wide property once for each user, so you may set it several times. To delete a user, remove that user from the file.

You can define this property in the usual ways--typically in the cloudscape.properties file. For more information about setting properties, see Tuning Cloudscape.

Here is a sample excerpt from the cloudscape.properties file:

# Users definition
#
cloudscape.user.sa=cloud3x9
cloudscape.user.mary=little7xylamb

Properties Summary

Table 8-1 summarizes the various properties related to user authentication.

Table 8-1 User Authentication Properties

Property Name

Use

cloudscape.connection.requireAuthentication

Turns on user authentication.

cloudscape.authentication.provider

Specifies the kind of user authentication to use.

cloudscape.authentication.server

For LDAP and NIS+ user authentication, specifies the location of the server.

cloudscape.authentication.ldap.searchAuthDN,
cloudscape.authentication.ldap.searchAuthPW,
cloudscape.authentication.ldap.searchFilter,
and cloudscape.authentication.ldap.searchBase

Configures the way DN searches are performed.

cloudscape.user.UserName

Creates a user name and password for Cloudscape's built-in user repository.

java.naming.*

JNDI properties

Programming Applications for Cloudscape User Authentication

Programming the Application to Provide the User and Password

In the DriverManager.getConnection call, an application can provide the user name and password:

NOTE: The password is not encrypted.When you are using Cloudscape in the context of a server framework, the framework should be responsible for encrypting the password across the network. If your framework does not encrypt the password, consider using SSL. Cloudconnector does not encrypt a password unless you use SSL.

For information about the treatment of user names within the Cloudscape system, see Users and Authorization Identifiers.

Login Failure Exceptions

If user authentication is turned on and a valid user name and password are not provided, SQLException XJ006 is raised.

ERROR XJ006: Login failed: Invalid username/password.