How can I control the userid of a remote process, such as a servlet, rmid process, or EJB running on behalf of a particular user?

Nathan Meyers

Developers sometimes need to write logic to be run in remote processes that must assume the permissions of a particular user on the remote machine. For example, you might want to write JSP or servlet pages that allow a user to manage his Linux account or access her home directory.

Background

This is a difficult problem for several reasons:

  • Unix/Linux userids are not a portable concept across all platforms that support Java.

  • Unix/Linux userids are not generally portable across a network, even a homogeneous network populated by just one platform.

  • Java provides almost no support for such non-portable features as Unix/Linux user authentication and authorization.

  • Native mechanisms for managing user identification, such as the Linux setuid() call, are not portable and do not work in consistent ways across different Java implementations or different OSes.

First, some brief (and highly simplified) background about user authorization. The Unix/Linux user authentication/authorization model is built on these basic ideas:

  • Every user has a unique numeric ID. This ID characterizes all processes run by the user and all files owned by the user. Processes run by a particular user can access that user's resources but have very limited access to the resources of other users.

  • There is one user, called the superuser, that has full rights and privileges to do anything to any process or any file. A process run by the superuser can fully access any user's resources and can impersonate any user.

Applying this information to a network daemon, such as a Web server, an RMI activation server, or an EJB container: the daemon can be run as a normal user or as the superuser. Running as superuser has some advantages - classes run by the daemon can control any user's resources needed to provide any service. But it's also a huge security hole that can compromise a system with the introduction of erroneous or malicious code. A much more common, and safer, solution is to create a unique userid to be used by a daemon or daemons.

Whatever solution is adopted - running as a normal user or superuser - it is the system administrator who chooses how to set up the daemon. The configuration decisions made by the system administrator determine how much power the Java code will be able to exercise.

The Problem

So back to the question: how can your classes - servlets, EJBs, RMIs, or whetever - gain the privileges they need to provide services to users?

In general, the answer is that they cannot: the daemon is usually running under its own safe userid, and the system will not let you impersonate other users.

A Solution

Unix and Linux systems provide a few special doors that allow users to assume the privileges of other users. For example, the su command allows you to execute commands as another user, if you know that user's password. The best, safest, and most portable way to assume other user privileges is to use one of these doors to run a program that acts with the needed user privileges.

One good choice is the sudo utility, a powerful and highly configurable way to assign user privileges for running programs. Sudo is not a standard Linux utility, but it is available in many Linux distributions and can also be found at rpmfind. It must be installed by the system administrator, and it must be configured to assign the needed privileges. The next section describes a sample configuration.

Example Solution Configuration

To explore a possible solution based on sudo, let's consider a simple problem: a servlet that allows a remote user to reboot the system.

Assume the servlet engine is run by a non-privileged user named "apache". This user cannot normally reboot the system, but a couple of entries in sudo's configuration file, sudoers, can change that:

  • Add a command alias specification for the reboot command:

        Cmnd_Alias REBOOT=/sbin/reboot
  • Add a privilege specification:

        apache  ALL=(ALL) NOPASSWD: REBOOT

This gives the user "apache" the power to reboot the system - without having to enter an additional password. Anyone logged in as this user could reboot by entering this command:

    sudo /usr/sbin/reboot

Your servlet can do the same thing - by executing that command through Runtime.exec():

    Runtime.getRuntime().exec("sudo /usr/sbin/reboot");

This is obviously a crude example, but it illustrates the basic point: sudo can assign specific privileges to specific users to run specific programs, possibly with specific options. In this example, it assigned superuser privileges to perform a reboot, but it can also assign privileges for other userids and other operations: you can tailor the privileges assigned by sudo to those your classes need to provide their services.

Conclusion

Dealing with Linux user authorization and authentication in server processes is not straightforward in Java. This FAQ has described the problem and presented one approach - running of privileged external programs - that can be used by server-based Java classes to get the privileges they need to provide their services.

Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.