Use of JAAS Login Utility and JavaTM
GSS-API for Secure Message Exchanges
This tutorial presents two sample applications
to demonstrate the use of the Java GSS-API. This API permits
secure exchanges of messages between communicating applications.
Here are the sample client and server applications you'll need for this tutorial:
Note: This tutorial uses the same client and server applications
as the Use of
JavaTM GSS-API for
Secure Message Exchanges Without JAAS Programming tutorial.
In that tutorial, JAAS (JavaTM Authentication and
Authorization Service) programming is not required. Instead, you let the
underlying mechanism decide how to get credentials.
This tutorial uses policy files and a more complex login configuration file.
The programs are run with a security manager; as a result, security-sensitive
operations are not allowed unless the required permissions
were explicitly granted. This tutorial also demonstrates how
JAAS authorization adds user-centric access control that applies
control based on who is running the code--not just on what code is running.
In this Java GSS-API tutorial, the first step is JAAS authentication.
Previous tutorials demonstrated the use of JAAS for user authentication
and authorization, and presented examples of policy files
and of login configuration files (specifying the underlying authentication
technology to be used) that JAAS requires. Applications in the JAAS introductory tutorials,
JAAS Authentication
and JAAS Authorization, made
direct calls to JAAS methods. The
Use of JAAS Login Utility tutorial showed
the use of a utility program that frees the application from having
to do this. The client and server applications in the current
tutorial also use the same utility program, so we recommend you read the login utility
tutorial first.
As with all tutorials in this series, the
underlying technology used to support authentication and
secure communication for the applications in this tutorial is Kerberos.
See Kerberos Requirements.
Overview of the Client and Server Applications
The applications for this tutorial are named SampleClient and
SampleServer.
Each is invoked by executing the Login utility supplied with this tutorial
and passing it as arguments the name of the application (SampleClient
or SampleServer), followed by the arguments needed by the
application. The Login utility uses a JAAS LoginContext to authenticate the
user using Kerberos. Finally, the Login utility
invokes the main method of the application class,
in our case either SampleClient or SampleServer, and passes the
application its arguments.
Here is a summary of execution of the SampleClient and SampleServer
applications:
Run the SampleServer application by
running the Login utility and passing it as arguments the name
"SampleServer" followed by the arguments for the
SampleServer program. The Login utility prompts you for the password
for the principal that SampleServer should run as. (See Kerberos User and Service Principal Names.)
After authentication is complete, SampleServer is run it:
Reads its argument, the port number that it should listen
on for client connections.
Creates a ServerSocket for listening for client
connections on that port.
Listens for a connection.
Run the SampleClient application (possibly on a different machine),
by running the Login utility and
passing it as arguments the name "SampleClient" followed by the
arguments for the SampleClient program. The Login utility prompts you for
your Kerberos name and password. After authentication is complete,
SampleClient is run. It
Reads its arguments: (1) The name of the Kerberos principal
that represents SampleServer. (See Kerberos
User and Service Principal Names.), (2) the name of the host (machine) on
which SampleServer is running, and (3) the port number on which
SampleServer listens for client connections.
Attempts a socket connection
with the SampleServer, using the host and port it was passed as arguments.
The socket connection is accepted by SampleServer and both
applications initialize a DataInputStream and a DataOutputStream from the
socket input and output streams, to be used for future data exchanges.
SampleClient and SampleServer each instantiate a GSSContext and
establish a shared context that will enable
subsequent secure data exchanges.
SampleClient and SampleServer can now securely exchange messages.
When SampleClient and SampleServer are done exchanging messages,
they perform clean-up operations.
Since the underlying authentication and secure communication technology
used by this tutorial is Kerberos V5, we use Kerberos-style
principal names
wherever a user or service is called for.
For example, when you run SampleClient you are asked to provide
your user name.
Your Kerberos-style user name is simply the user name you were assigned for
Kerberos authentication. It consists
of a base user name (like "mjones") followed by an "@" and
your realm (like "mjones@KRBNT-OPERATIONS.ABC.COM").
A server program like SampleServer is typically considered to offer a
"service" and to be run on behalf of a particular "service principal."
A service principal name for SampleServer is needed in several places:
When you run SampleServer you must log in as the appropriate service
principal. The login configuration file for this tutorial actually specifies the
service principal name (as an option to the Krb5LoginModule),
so the JAAS authentication (done by the Login utility)
just asks you to specify the password for that service principal. If you specify
the correct password, the authentication is successful, a
Subject is created containing a Principal with the service principal name,
and that Subject is associated with a new access control context.
The subsequently-executed code (the SampleServer code) is considered to be
executed on behalf of the specified principal.
When you run SampleClient, one of the arguments is the service principal
name. This is needed so SampleClient can initiate establishment of a
security context with the appropriate service.
The client and server policy files each require a ServicePermission
with name equal to the service principal name and action equal to
"initiate" or "accept" (for initiating or accepting establishment of a
security context).
Throughout this document, and in the accompanying login configuration file
and policy files,
service_principal@your_realm
is used as a placeholder to be replaced by the actual name to be
used in your environment.
Any Kerberos principal can actually be used for the service principal
name. So for the purposes of trying out this tutorial, you could use
your user name as both the client user name and the service principal name.
In a production environment, system administrators typically like servers to
be run as specific principals only and may assign a particular name to be used.
Often the Kerberos-style service principal name assigned is of the form
service_name/machine_name@realm;
For example, an nfs service run on a machine named "raven" in
the realm named "KRBNT-OPERATIONS.ABC.COM" could have the service principal
name
nfs/raven@KRBNT-OPERATIONS.ABC.COM
Such multi-component names are not required, however. Single-component
names, just like those of user principals, can be used. For example, an
installation might use the same ftp service principal ftp@realm
for all ftp servers in that realm, while another installation might
have different ftp principals for different ftp servers, such as
ftp/host1@realm and ftp/host2@realm on
machines host1 and host2, respectively.
When the Realm is Required in Principal Names
If the realm of a user or service principal name is the default realm (see
Kerberos Requirements), you can
leave off the realm when you are logging into Kerberos (that is, when
you are prompted for your username). Thus, for example, if
your user name is "mjones@KRBNT-OPERATIONS.ABC.COM", and you run
SampleClient, when it requests your user name you could just specify
"mjones", leaving off the realm. The name is interpreted in the context of
being a Kerberos principal name and the default realm is appended, as
needed.
You can also leave off the realm if a principal name will be converted to a
GSSName by a GSSManager createName method. For example, when
you run SampleClient, one of the arguments is the server service
principal name. You can specify the name without including the realm,
because SampleClient passes the name to such a createName method,
which appends the default realm as needed.
It is recommended that you always include realms when principal names
are used in login configuration files and policy files, because the
behavior of the parsers for such files may be implementation-dependent;
they may or may not append the default realm before such names are utilized
and subsequent actions may fail if there is no realm in the name.
The Login Configuration File
Whenever JAAS is used, a login configuration is required to
specify the desired authentication technology.
(See JAAS Login Configuration File
for more information about what a login configuration file is.)
Both SampleClient and SampleServer can use the same
login configuration file, if that file contains
two entries, one entry for the client side and one for the server side.
The csLogin.conf login configuration file
used for this tutorial is the following:
Note that the name for each entry matches the respective
class names for our two top-level applications,
SampleClient and SampleServer. Recall that this is also the name that is
passed to the Login utility that performs JAAS operations for the
application. That utility expects the name of the entry to be looked up
in your login configuration file to be the same as the
name it is passed.
Both entries specify that Sun's Kerberos V5 LoginModule
must be used to successfully authenticate the user.
The Krb5LoginModule succeeds only if the attempt to log in to the
Kerberos KDC as a specified entity is successful. In the case of
SampleClient, the user will be prompted for their name and password.
In the case of SampleServer, a name is already supplied
in this login configuration file (the specified principal, as described
below)
and the user running SampleServer is just asked for the password for the
entity specified by that name. They must specify the correct password
in order for authentication to succeed.
The SampleServer entry storeKey=true indicates that
a secret key should be calculated from the password provided during login
and it should be stored in the private credentials of the Subject created
as a result of login.
This key is subsequently utilized during mutual authentication
when establishing a security context between SampleClient and SampleServer.
The Krb5LoginModule has a principal option that can be
used to specify that only the specified principal (entity/user) should be
logged in for the given program. Here, the SampleClient entry does not
specify a principal (although it could, if desired), so the user is prompted
for a user name and password
and anyone with a valid user name and password
can run SampleClient. SampleServer, on the other hand, indicates a
particular principal because system administrators usually like servers
to be run as specific principals only.
In this case, the user running SampleServer is prompted for that
principal's password and must supply the correct one in order for
authentication to succeed.
Note that you must replace "service_principal@your_realm"
with the name of the service principal that represents
SampleServer. (See Kerberos User and Service
Principal Names.)
For information about all the possible options that can be
passed to Krb5LoginModule, see the
Krb5LoginModule documentation.
The Policy Files
The policy file used when running SampleClient is
client.policy, and the
policy file used when running SampleServer is
server.policy.
Their contents are described below.
The Client Policy File
Permissions Required by the Login utility Classes
A number of permissions are required by the classes in
Login.java (Login and MyAction).
As recommended in the previous tutorial
on the use of Login, we
create a Login.jar JAR file containing the Login.class
and MyAction.class files and in the
client.policy policy file we grant
Login.jar AllPermission:
grant codebase "file:./Login.jar" {
permission java.security.AllPermission;
};
Permissions Required by SampleClient
The SampleClient code does two types of operations for
which permissions are required. It
opens a socket connection with the host machine running the
SampleServer application.
initiates establishment of a security context with SampleServer.
The permission required to open a socket connection is
You may replace the "*" with the hostname or IP address
of the machine that SampleServer will be running on.
The permission(s) required to initiate establishment of a security context
will depend on the underlying mechanism. This tutorial
uses Kerberos as the underlying mechanism, and for that two
javax.security.auth.kerberos.ServicePermissions are
required. A ServicePermission contains a service principal name and
an action (or list of actions). To initiate establishment of a security context,
you need two ServicePermissions with action "initiate", whose names
specify:
the service principal name for the ticket granting service for
your realm. Granting this permission essentially allows the use of
Kerberos as a client.
the service principal name representing
SampleServer. (See Kerberos User and Service
Principal Names.) Granting this permission allows you to interact
with the service, SampleServer, using Kerberos.
We want to grant the permissions to a specific authenticated user
executing SampleClient, so we specify both the SampleClient
code location (in SampleClient.jar) and a Principal designation
indicating the user name and realm for the user (you, the person
who will run SampleClient). (See
How Do You Make Principal-Based Policy File
Statements? in the second tutorial of this series for information
on policy file grant statements that include Principal
designations.)
Here is the basic form for the grant statement:
grant CodeBase "file:./SampleClient.jar",
Principal javax.security.auth.kerberos.KerberosPrincipal
"your_user_name@your_realm" {
permission java.net.SocketPermission "*", "connect";
permission javax.security.auth.kerberos.ServicePermission
"krbtgt/your_realm@your_realm",
"initiate";
permission javax.security.auth.kerberos.ServicePermission
"service_principal@your_realm",
"initiate";
};
You must substitute your Kerberos user name (complete with "@" and realm)
for "your_user_name@your_realm". For example, if your
user name is "mjones" and your realm is "KRBNT-OPERATIONS.ABC.COM",
you would use "mjones@KRBNT-OPERATIONS.ABC.COM" (complete with the quotes).
You must also substitute your realm in "krbtgt/your_realm@your_realm"
and the service
principal name for the service principal representing the server for
"service_principal@your_realm".
Suppose the former is "krbtgt/KRBNT-OPERATIONS.ABC.COM@KRBNT-OPERATIONS.ABC.COM"
and the latter is "sample/raven.abc.com@KRBNT-OPERATIONS.ABC.COM",
and your user name is as specified in the previous paragraph.
Then the grant statement would be
grant CodeBase "file:./SampleClient.jar",
Principal javax.security.auth.kerberos.KerberosPrincipal
"mjones@KRBNT-OPERATIONS.ABC.COM" {
permission java.net.SocketPermission "*", "connect";
permission javax.security.auth.kerberos.ServicePermission
"krbtgt/KRBNT-OPERATIONS.ABC.COM@KRBNT-OPERATIONS.ABC.COM",
"initiate";
permission javax.security.auth.kerberos.ServicePermission
"sample/raven.abc.com@KRBNT-OPERATIONS.ABC.COM",
"initiate";
};
We want to grant the permissions to a specific authenticated user
executing SampleServer (the service principal considered to represent
SampleServer), so we specify both the SampleServer
code location (in SampleServer.jar) and a Principal designation
indicating the service principal.
Suppose this name is
"sample/raven.abc.com@KRBNT-OPERATIONS.ABC.COM". Then the
grant statement would be
grant CodeBase "file:./SampleServer.jar"
Principal javax.security.auth.kerberos.KerberosPrincipal
"sample/raven.abc.com@KRBNT-OPERATIONS.ABC.COM" {
permission java.net.SocketPermission "*", "accept";
permission javax.security.auth.kerberos.ServicePermission
"sample/raven.abc.com@KRBNT-OPERATIONS.ABC.COM", "accept";
};
Running the SampleClient and SampleServer Programs
To execute the SampleClient and SampleServer programs, do the
following:
In both places it appears, replace "service_principal@your_realm"
in server.policy
with the Kerberos name of the service principal that represents SampleServer.
(The same name as that used in the login configuration file.)
Compile Login.java and SampleServer.java:
javac Login.java SampleServer.java
Note that Login.java contains two classes and thus compiling
Login.java creates Login.class and
MyAction.class.
Create a JAR file named Login.jar containing
Login.class and MyAction.class:
jar -cvf Login.jar Login.class MyAction.class
Create a JAR file named SampleServer.jar containing
SampleServer.class:
jar -cvf SampleServer.jar SampleServer.class
Prepare SampleClient for Execution
To prepare SampleClient for execution, do the following:
Copy the following files into a directory accessible by the machine
on which you will run SampleClient:
Create a JAR file named Login.jar containing
Login.class and MyAction.class:
jar -cvf Login.jar Login.class MyAction.class
Create a JAR file named SampleClient.jar containing
SampleClient.class:
jar -cvf SampleClient.jar SampleClient.class
Execute SampleServer
It is important to execute SampleServer before SampleClient because
SampleClient will try to make a socket connection to SampleServer and that
will fail if SampleServer is not yet running and accepting socket
connections.
To execute SampleServer, be sure to run it on the machine it is
expected to be run on. This machine name (host name) is specified as an
argument to SampleClient. The service
principal name appears in several places, including the login configuration file
and the policy files.
Go to the directory in which you have prepared SampleServer for
execution. Execute the Login class, specifying
by an appropriate -classpath clause that classes should be
searched for in the Login.jar and SampleServer.jar
JAR files,
by -Djava.security.manager that a
security manager should be installed,
by -Djava.security.krb5.realm=<your_realm> that
your Kerberos realm is the one specified. For example, if your realm is
"KRBNT-OPERATIONS.ABC.COM" you'd put
-Djava.security.krb5.realm=KRBNT-OPERATIONS.ABC.COM.
by -Djava.security.krb5.kdc=<your_kdc> that
your Kerberos KDC is the one specified. For example, if your KDC is
"samplekdc.abc.com" you'd put
-Djava.security.krb5.kdc=samplekdc.abc.com.
by -Djava.security.policy=server.policy that
the policy file to be used is server.policy, and
by -Djava.security.auth.login.config=csLogin.conf that the
login configuration file to be used is csLogin.conf.
You pass the name of your application (in this case, "SampleServer") as an
argument to Login. You then add as arguments any arguments required by
your application, which in the case of SampleServer is a single argument
specifying the port number to be used for listening for client
connections. Choose a high port number unlikely to be used for anything else.
An example would be something like 4444.
Below are the full commands to use for both Microsoft Windows and
Unix systems.
The only difference is that on Microsoft Windows systems you use semicolons to
separate classpath items, while you use colons for that purpose on
Unix systems.
Important: In these commands, you must replace
<port_number> with an appropriate port number,
<your_realm> with your Kerberos realm,
and <your_kdc> with your Kerberos KDC.
Here is the command for Microsoft Windows systems:
Type the full command on one line. Multiple lines are used here
for legibility. If the command is too long for your system, you may
need to place it in a .bat file (for Microsoft Windows) or a .sh file
(for UNIX) and then run that file to execute the command.
You will be prompted for the Kerberos password for the
service principal.
The underlying Kerberos authentication mechanism
specified in the login configuration file will log the service principal into
Kerberos. Once authentication is successfully completed,
the SampleServer code will be executed
on behalf of the service principal.
It will listen for socket connections on the specified port.
To execute SampleClient, go to the directory in which you have prepared
SampleClient for execution. Then execute the Login class,
specifying
by an appropriate -classpath clause that classes should be
searched for in the Login.jar and SampleClient.jar
JAR files,
by -Djava.security.manager that a
security manager should be installed,
by -Djava.security.krb5.realm=<your_realm> that
your Kerberos realm is the one specified.
by -Djava.security.krb5.kdc=<your_kdc> that
your Kerberos KDC is the one specified.
by -Djava.security.policy=client.policy that
the policy file to be used is client.policy, and
by -Djava.security.auth.login.config=csLogin.conf that the
login configuration file to be used is csLogin.conf.
Pass to Login the name of your application ("SampleClient") followed
by the arguments required by SampleClient.
The SampleClient arguments are (1) the Kerberos name of
the service principal that represents SampleServer, (2) the name of the host
(machine) on which SampleServer is running, and (3) the port number on which
SampleServer is listening for client connections.
Below are the full commands to use for both Microsoft Windows and Unix systems.
Important: In these commands, you must replace
<service_principal>,
<host>, <port_number>,
<your_realm>, and <your_kdc>
with appropriate values (and note that the port number must be
the same as the port number passed as an argument to SampleServer).
These values need not be placed in quotes.
Here is the command for Microsoft Windows systems:
Type the full command on one line. Multiple lines are used here
for legibility. As with the command for executing SampleServer,
if the command is too long to type directly into your command window,
place it in a .bat file (Microsoft Windows) or a .sh file (UNIX) and then
execute that file.
When prompted, type your Kerberos user name and password.
The underlying Kerberos authentication mechanism
specified in the login configuration file will log you into
Kerberos. Once authentication is successfully completed,
the SampleClient code will be executed
on behalf of you. It will request a socket connection with
SampleServer. Once SampleServer accepts the connection,
SampleClient and SampleServer establish a shared context and then
exchange messages as described in this tutorial.