Scribblin.gs
a blob   Software

SysAuth

In a multi-user environment, if is often necessary for a program to be able authenticate users, that is, to check some credentials and allow or deny access to a resource or operation depending on the outcome of the check. The ubiquitous login screen found on virtually every web application is indicative of the requirement for this functionality. Java now has a sophisticated system for authenticating users, namely the JAAS API. However, whilst this API is very comprehensive, it is somewhat heavyweight for a small application with simple authentication requirements. Hence the creation of SysAuth.

SysAuth provides a lightweight, simple alternative to JAAS's basic authentication functions. When presented with a username-password pair, it checks the credentials using the native authentication system of the underlying OS. Specifically, on a windows machine, it relies on the Windows Domain Controller to determine authenticity; on a Linux machine, it uses PAM. The SysAuth API is trivial; authentication can be accomplished with very little effort. As the API is agnostic with respect to the underlying OS, no code changes are needed if an application written, say, for Windows needs to be ported to a machine running Linux.

I have tested SysAuth on Windows 2000 and on Debian GNU/Linux; binaries are available for download for both of these platforms. As the Linux version uses PAM, it should work on any Unix machine with PAM and Java support; however, I have not tested this hypothesis nor attempted to compile on any such platform.

Read about Installation
Read about Building Sysauth on Linux
Read about Building Sysauth on Windows
Read about Using Sysauth
Read about Troubleshooting Sysauth
Read acknowledgements
Download SysAuth

Installation

Installing SysAuth is simple: it involves merely ensuring that the Java component (sysauth.jar) is placed somewhere on the Classpath, and that the native code component (libSysAuth.so on Linux, or SysAuth.dll on Windows) resides on the Library Path. Alternatively, SysAuth can be used without being "installed" as such: just ensure that the SysAuth code is available on the Classpath and library path of the application that is using it. This can be accomplished with an invocation such as: java -cp sysauth.jar -Djava.library.path="/path/to/libSysAuth.so".

To authenticate with SysAuth on Linux, PAM must be configured for the "java_auth" service. If you wish SysAuth to authenticate users using the standard Unix authentication mechanisms (i.e. via /etc/passwd and /etc/shadow), the following sample java_auth PAM configuration file should be sufficient:

# java_auth - PAM configuration for the gs.scribblin.sysauth package
# save this file in /etc/pam.d with the name "java_auth"

# SysAuth only uses the authentication part of PAM, so we have no need
# for account and session entries here. This authentication entry uses
# standard unix-style authentication; the "nullok" option means that we allow
# users to have null passwords.
auth    required  pam_unix.so nullok

Read the PAM documentation for more information about the format and contents of this file.

SysAuth has a command-line mode, designed for testing that the system is working properly. Type java gs.scribblin.sysauth.SysAuth uname passwd where uname and passwd are a valid username/password pair on your system. SysAuth should report "Access Granted"; an incorrect set of credentials should evoke "Access Denied". If Java complains of a NoClassDefFoundError, SysAuth cannot be found on the classpath, so either move it to a location where it can be found, or invoke java with -cp /path/to/sysauth.jar . If Java complains of an UnsatisfiedLinkError, the native part of SysAuth cannot be found; ensure that libSysAuth.so or SysAuth.dll can be found on the library path or invoke java with -Djava.library.path="/path/to/sysauthlib". Finally, if SysAuth claims "Access Denied" when you were expecting "Access Granted", panic not but read the troubleshooting section further down this page.

Building SysAuth on Linux

Building SysAuth from the sources requires a Java compiler, a C++ compiler and (optionally) GNU Make. The procedure is as follows: First, untar the source package. Then change into the top-level directory and, using your favourite editor, open Makefile. The very first line in this file sets the variable JDK_HOME; change it to point to the root of your JDK installation. Then save the changes and invoke make. After a little whirring and gnashing of compilers, you should find sysauth.jar sitting in the current directory, and libSysAuth.so in the "native" subdirectory. If you do not have GNU Make, look inside the Makefile and manually carry out the commands found within it. Alternaively, install GNU Make.

Building SysAuth on Windows

If you wish to build the code under Windows, you will need a C++ compiler, such as Microsoft VC++ and a JDK. If you are not using VC++, you will also need to ensure that a copy of advapi.lib is available to your linker.

There is no Makefile for Windows compilation. Therefore, SysAuth must be built manually. First, untar the archive and change to the "src" directory. Compile the Java file with: javac SysAuth.java. Next, move the resulting SysAuth.class file into a "classes\gs\scribbin\sysauth" directory (path relative to the root of the archive). In order to create a Jar file, which is not necessary but makes classpaths more manageable, use the following command (which assumes that your current directory is still "src"): jar cvf ..\sysauth.jar ..\classes\*. This should create sysauth.jar in the root of the unpacked archive directory. To compile the native code, change directory to "src\native". Then run VCVARS.BAT, a batch file that ships with VC++ and can be found in the "bin" directory of the VC++ distribution. This batch file sets up the array of environment variables necessary to run VC++ from the command line. To create SysAuth.dll, issue the following commands:

set JDK_HOME=c:\jdk1.2 (or the path to your JDK if different)
cl  -I%JDK_HOME%\include -I%JDK_HOME%\include\win32
    -LD gs_scribblin_sysauth_SysAuth.cpp advapi32.lib
    -FeSysAuth.dll -DMSWINDOWS

Note that the cl.exe options should all appear on a single line; I have split them here for the sake of readability. Once the command stops executing, you should find a SysAuth.dll in the working directory. If you are not using VC++, I am sorry to say that I cannot help you with the native code compilation; you will have to work out the equivalent incantations yourself.

Using SysAuth in Programs

Using SysAuth is simple. The library consists of a single class, gs.scribblin.sysauth.SysAuth which has two static methods, both of which return a boolean indicating that access should be allowed (true) or denied (false). The simpler of the two methods is:

  boolean isAllowed(String username, String password);

Using the underlying OS authentication system (domain controller or PAM), this method attempts to authenticate the user specified by username with the password specified by password. On Linux, authentication is carried out according to the PAM configuration; on Windows, authentication is performed against the "default" domain (i.e. the domain of which the machine running SysAuth is a member). The other method is:

  boolean isAllowed(String username, String password, String domain);

As the signature suggests, this method allows specification of a Windows Domain against which the credentials should be checked. If domain is null, the behaviour is identical to the two-parameter method. When running on Linux, the domain parameter is simply ignored.

The following program illustrates SysAuth's use. It takes a username and password as command-line parameters, and attempts to authenticate:

import gs.scribblin.sysauth.*;

public class SysAuthTest
{
    public static void main(String[] args)
    {
        if (args.length != 2){
            System.err.println("I need a username and password");
            return;
        }

        if (SysAuth.isAllowed(args[0], args[1])){
            System.out.println("Access allowed!");
        } else {
            System.out.println("Access denied!");
        }
    }
}

Before attempting to compile this or any other code that uses SysAuth, remember to ensure that sysauth.jar is on the classpath used by the compiler, and that libSysAuth.so (on Linux) or SysAuth.dll (on Windows) resides on the library path.

Troubleshooting

SysAuth relies on authentication APIs made available by the underlying operating system upon which the JVM is running. In order to work correctly, these APIs may require the program using SysAuth to be running with rather more privileges than would normally be granted to a standard user. If this requirement is not met, SysAuth cannot authenticate a user and the isAllowed() methods will return false regardless of the credentials passed to them. If an attempt to test a SysAuth installation using the procedure outlined earlier in this document failed, lack of privileges could well be the reason.

On Windows servers, any program that wishes to invoke the LogonUser() API function, which SysAuth calls internally, must have permission to "Act As Part of the Operating System". This is a so-called "advanced" user right that can be granted to accounts on an individual basis. I recommend that an application using SysAuth should be allotted its own user account and given this permission; the account does not require Domain Admin privileges.

In principle, any user can use SysAuth on Linux, as PAM itself is agnostic as to the credentials of its caller. In practice, however, the requirements depend on the specifics of the PAM module being used. On a system with shadow passwords, pam_unix can only authenticate a user if it has access to the file /etc/shadow or its equivalent. If an attempt to test a SysAuth installation fails, ensure that the user running SysAuth has permission to read /etc/shadow. On many systems, this means adding the user to a "shadow" group.

If you wish to write a web application, run it on Linux (for example, using Tomcat and Apache) but authenticate from a Windows 2000 domain, this can be accomplished admirably with Frank Cusak's kerberos PAM module; for basic authentication functions, Microsoft's Kerberos appears to be entirely compatible with MIT Kerberos V.5. No special permissions are needed to use SysAuth with PAM kerberos. There is, however, one small caveat: by default, pam_kerberos does not merely attempt to authenticate a set of credentials with a Kerberos server; it also checks that the credentials belong to a user with a local system account. More specifically, the module calls getpwnam() and returns PAM_USER_UNKNOWN if the call fails. This is something of a show-stopper if your users do not have accounts on the Linux server. A quick fix for this behaviour is as follows: download the sources to pam_kerberos and locate the following code fragment in pam_kerb5_auth.c:

 pw = getpwnam(name);
 if (!pw) {
    DLOG("getpwnam()", lname);
    pamret = PAM_USER_UNKNOWN;
    goto cleanup2;
 }

Delete these lines and compile the modified pam_kerberos.

Acknowledgements

The PAM "conversation" function found in gs_scribblin_sysauth_pam.cpp is based on code written by Ingo Luetkebohle for his mod_auth_pam Apache module. I am grateful to Ingo for allowing me to use his code under the terms of the GPL, thereby enabling its modified inclusion here. I am also grateful to Dave Townsend for prompting me to modify SysAuth to use PAM with dlopen() et al (so that it behaves better on non-Debian Linux distributions).

Downloading SysAuth

SysAuth is released under the terms of the GNU General Public Licence Version 2.

Download sysauth.jar
Download libSysAuth.so for Linux
Download SysAuth.dll for Windows
Download the Source Code