In this chapter, system properties of JVM
are shown in curly brackets: {user.name}
; other variables are in angle brackets:
<uid>
.
Traditionally, people at Fermilab use various types of authentication programs that store cached tickets differently. The generic implementation of Kerberos in J2SE SDK is missing a functionality that could examine several possible locations in series, in order to find a single valid ticket. Memory caches are not supported, too. Finding a solution of these issues was the initial goal for the project. Later, some additional functions have been implemented for user convenience. At this time, Kerberos Module can:
The password authentication is not supported in Kerberos Module for a pupose. This is done in order to avoid compromising of the password when it is accidentally entered in X terminal.
The code is divided into the client, server, and common parts. They are located in the following packages:
gov.fnal.controls.applets.kerberos |
Applet for user authentication over HTTP. | client |
gov.fnal.controls.tools.kerberos |
High-level configuration and authentication for users. | common, client |
gov.fnal.controls.tools.kerberos.base |
Low-level login modules and cache readers. | common |
gov.fnal.controls.tools.kerberos.realm |
Tomcat valves for HTTP authentication. | server |
gov.fnal.controls.tools.kerberos.server |
High-level classes for services. | server |
Common and client parts do not have external dependencies.
The server-side code may require:
gov.fnal.controls.tools.kerberos.realm
gov.fnal.controls.db.*
gov.fnal.controls.tools.kerberos.server
The code is combined in the following archives:
gov.fnal.controls.tools.kerberos
gov.fnal.controls.tools.kerberos.base
gov.fnal.controls.applets.kerberos
gov.fnal.controls.tools.kerberos
gov.fnal.controls.tools.kerberos.base
gov.fnal.controls.applets.kerberos
gov.fnal.controls.tools.kerberos
gov.fnal.controls.tools.kerberos.base
gov.fnal.controls.tools.kerberos.realm
gov.fnal.controls.tools.kerberos.server
Let's discuss some common scenario.
On the client side, there sit a human being, who has either the cached ticket or, in few cases, the keytab. This user is running our program and we want to:
On the server, we want to:
These basic operations (except message exchange) are implemented in two classes,
ClientLoginContext
and
ServiceLoginContext
, that
need to be run on client and server sides, correspondingly.
On the client side, use:
ClientLoginContext.getInstance().login();
The login
method may fail if the routine can not find the valid ticket in caches
or obtain it using a keytab. Call logout()
when the credential is not needed anymore.
A credential in our case is the ticket-granting ticket (TGT).
It contains a big piece of encrypted data intended only for
TGS,
plus several plain-text attributes, such as the principal name, expiration date,
and others. To get the principal name and expiration date directly from
ClientLoginContext
, use getPrincipalName()
and
getExpiration()
methods. The user principal name always includes a domain,
e.g., apetrov@FNAL.GOV
(and by the way, this is my principal,
not necessarily my e-mail).
It is important to understand, that
TGT
alone can not work as a proof of identity, because it is no more then some binary data stored
in a file or the memory (unless a keytab was used). All its unencrypted attributes, including
the principal name, can easily be faked up. Only
(TGS) has a secret key to decrypt the ticket
body and make sure that everything is correct. This means that the result of
getPrincipalName()
can be trusted only when the next step
(sending/accepting credential) has succeeded.
On the server side, we also have to get a credential for the running process (service)
in order to be able to accept the user credential. (This operation was not reflected
in the above list, because there was only what we wanted to do).
Services can be authenticated only with keytabs. The format of a service principal name is:
<service>/<host>@<realm>
e.g., host/outland.fnal.gov@FNAL.GOV
. Like users, service authentication is started
with login
method.
Authentication on the clients and on the servers works in slightly different ways:
ClientLoginContext
class authenticates user principals.
First of all, it tries to find the
ticket in caches, looking over several possible locations
for the given operating system. Once a well-formatted and unexpired ticket is found, the module
stops the search. If no cached tickets were found, the program then tries to use
a keytab file.
ServiceLoginContext
class authenticates service principals,
only using a keytab file.
There are few common configurations of Kerberos clients that can be found on most of the computers. On all UNIXes there is kinit program; it keeps tickets in a file. On Mac OS X kinit stores tickets in the memory. On Windows, there is either a MIT suite that includes Leash32 and kinit and stores tickets mostly in the memory, or WRQ Reflection program that uses a file cache.
For a given operating system, Kerberos Module tries to find the valid ticket in several possible locations:
Operating System | # | Location |
---|---|---|
Free BSD | 1 | $KRB5CCNAME 1 |
2 | FILE:/tmp/krb5cc_<uid> |
|
Linux | 1 | $KRB5CCNAME |
2 | FILE:/tmp/krb5cc_<uid> |
|
Mac OS X | 1 | API: |
Sun OS | 1 | $KRB5CCNAME |
2 | FILE:/tmp/krb5cc_<uid> |
|
Windows | 1 | API: 3 |
2 | %KRB5CCNAME% |
|
3 | FILE:{java.io.tmpdir}\krb5cc |
|
4 | FILE:{user.home}\My Documents\Reflection\{user.name}@FNAL.GOV.cch |
|
5 | FILE:{user.home}\My Documents\Reflection\{user.name}.cch |
1 The value of KRB5CCNAME
environment variable must
include either API:
or FILE:
prefix.
2 Accessed through native Kerberos support on Mac OS X.
3 Accessed through krbcc32.dll
.
The user credential can be sent to another machine by means of
GSS-API. As a result, two peers create a
common security context. In reality this means that both the client
and the server obtain instances of
org.ietf.jgss.GSSContext
class. This class allows a program
to use some security services for data exchange. In the simpliest case, possession of
an instance of this class can be used as a proof of the user identity. The user name
can be obtained through getSrcName().toString()
call. If two nodes need
to establish some sort of an encrypted or reliable communication, GSSContext
provides several per-message security services.
An initiative to create GSSContext
always comes from the client side.
ClientLoginContext
class has several request
methods that
need to be used for this. In ServiceLoginContext
, the accept
method is used to create a server-side instance of the context. In order to perform
request and accept operations, both parties have to be logged
in, but this is not required explicitly—login
is automatically called
whenever the principal is not authenticated or its lifetime has expired.
In order to negotiate the context, two peers need to exchange some data, also known as
tokens.
The core implementation of request
and accept
methods uses
java.nio.channels.Channel
classes as transport
level abstractions; in a concrete program these classes can be easily linked
through a socket, or something like this.
The request
method is also used to specify a particular set of security services,
such as:
If either confidentiality or integrity is requested, the authentication has to be mutual,—two peers need to show each other their credentials in order to obtain some common encryption key. To complete the mutual authentication, they need to exchange tokens several times. In practice, there are three transactions. Otherwise, if mutual authentication is not required, the requestor needs to send only a single token to the accepting host.
GSSContext
has an important function of wrapping and unwrapping data
that can be used in secure data exchange. Wrapping means either encryption or
adding a Message Integrity Code (MIC). While encryption is to
conceal the data, MIC works as a digital signature that prevents data from being
altered. GSSContext
's methods are wrap
and unwrap
.
All security services should be specified at the time when GSSContext
is initially requested on the client side, using boolean attributes of the
request
method.
In order to work properly on the server and client sides, Kerberos Module generally requires
no or very little changes in its configuration. If, for some reason, you have to
change the default settings, this can done through
JVM system properties. New values of
the properties should be set before the very first usage of any class
included in the module. It is desirable to use command line arguments:
-D<property-name>=<property-value>
.
General-purpose properties are:
FNAL.GOV.
i-krb-7.fnal.gov:88.
daeset\/bd.
{user.home}/krb5.keytab
.
In order to make sure that Kerberos Module works properly in a particular configuration,
principal are registeres, keytabs available and so on, one can use both
ClientLoginContext
and
ServiceLoginContext
as standalone test programs,
launching them from the command line. In this mode, they are performing two functions:
In both cases, -h
option will show you the list of possible commands.
The source code of these classes can also be used as an example.
Suppose your keytab file is /tmp/my_keytab
. In order to figure out whether
the server is configured properly, use -a
option
(“authenticate only”):
bash-2.05$ java -cp /usr/local/dae/jars/split/krb5realm.jar -Dgov.fnal.controls.tools.kerberos.keytab=/tmp/my_keytab gov.fnal.controls.tools.kerberos.server.ServiceLoginContext -a Settings: Current version: 3.7.4 java.security.auth.login.config=jar:file:/usr/local/dae/releases/v2_3_0_1714/ jars/split/krb5realm.jar!/gov/fnal/controls/tools/kerberos/login.ini java.security.krb5.realm=FNAL.GOV java.security.krb5.kdc=i-krb-7.fnal.gov:88 gov.fnal.controls.tools.kerberos.keytab=/tmp/my_keytab gov.fnal.controls.tools.kerberos.principal=daeset/bd/foo.fnal.gov@FNAL.GOV Starting authentication Nov 22, 2004 3:29:07 PM gov.fnal.controls.tools.kerberos.AbstractLoginContext login INFO: Kerberos login started Nov 22, 2004 3:29:08 PM gov.fnal.controls.tools.kerberos.base.KrbKeytabLoginModule login INFO: Using keytab /tmp/my_keytab Nov 22, 2004 3:29:08 PM gov.fnal.controls.tools.kerberos.AbstractLoginContext login INFO: Authenticated as daeset/bd/foo.fnal.gov@FNAL.GOV till null Service authenticated (299ms) Finished
Now let's start the server in a mode, when it is listening to an IP port and acceping incoming GSS requests (no command line arguments in this case):
bash-2.05$ java -cp /usr/local/dae/jars/split/krb5realm.jar -Dgov.fnal.controls.tools.kerberos.keytab=/tmp/my_keytab gov.fnal.controls.tools.kerberos.server.ServiceLoginContext Settings: Current version: 3.7.4 java.security.auth.login.config=jar:file:/usr/local/dae/releases/v2_3_0_1714/ jars/split/krb5realm.jar!/gov/fnal/controls/tools/kerberos/login.ini java.security.krb5.realm=FNAL.GOV java.security.krb5.kdc=i-krb-7.fnal.gov:88 gov.fnal.controls.tools.kerberos.keytab=/tmp/my_keytab gov.fnal.controls.tools.kerberos.principal=daeset/bd/foo.fnal.gov@FNAL.GOV Opening channel on port 4747 Waiting for new client request...
On the client side, we do not need to have keytabs, but ought to have the valid ticket in a cache. If you have not done this yet:
[apetrov@xxx apetrov]$ kinit Password for apetrov@FNAL.GOV:
After that, we are going to use Kerberos Module to get our local
credential and present it to the server foo.fnal.gov
(for mutual authentication add +s
and/or +i
options):
[apetrov@xxx apetrov]$ java -cp /usr/local/dae/jars/split/krb5.jar gov.fnal.controls.tools.kerberos.ClientLoginContext -g foo.fnal.gov Settings: Current version: 3.7.4 java.security.auth.login.config=jar:file:/usr/local/dae/jars/split/krb5.jar! /gov/fnal/controls/tools/kerberos/login.ini java.security.krb5.realm=FNAL.GOV java.security.krb5.kdc=i-krb-7.fnal.gov:88 gov.fnal.controls.tools.kerberos.keytab=/home/apetrov/krb5.keytab gov.fnal.controls.tools.kerberos.principal=apetrov@FNAL.GOV Starting authentication Nov 22, 2004 4:01:31 PM gov.fnal.controls.tools.kerberos.AbstractLoginContext login INFO: Kerberos login started Nov 22, 2004 4:01:31 PM gov.fnal.controls.tools.kerberos.base.KrbCacheReader readTicket INFO: Using ticket cache FILE:/tmp/bar Nov 22, 2004 4:01:31 PM gov.fnal.controls.tools.kerberos.base.KrbCacheReader readTicket INFO: Ticket obtained Nov 22, 2004 4:01:31 PM gov.fnal.controls.tools.kerberos.AbstractLoginContext login INFO: Authenticated as apetrov@FNAL.GOV till Tue Nov 23 11:01:24 CST 2004 Client authenticated (199ms) Login options: integrity= false confidentiality= false replayDetection= false sequenceDetection=false Connecting to foo.fnal.gov:4747 Starting GSS transaction with daeset\/bd@foo.fnal.gov User authenticated! (663ms) established=true protReady=true srcName=apetrov@FNAL.GOV targName=daeset\/bd@foo.fnal.gov mutualAuthState=false integState=false confState=false replayDetState=false sequenceDetState=false lifetime=2147483647 Finished
At the same time, on the server side we will see something like:
Request received from /131.225.XXX.XXX:33989 Starting service authentication... Nov 22, 2004 4:01:32 PM gov.fnal.controls.tools.kerberos.AbstractLoginContext login INFO: Kerberos login started Nov 22, 2004 4:01:32 PM gov.fnal.controls.tools.kerberos.base.KrbKeytabLoginModule login INFO: Using keytab /tmp/my_keytab Nov 22, 2004 4:01:32 PM gov.fnal.controls.tools.kerberos.AbstractLoginContext login INFO: Authenticated as daeset/bd/foo.fnal.gov@FNAL.GOV till null Service authenticated (290ms) Starting GSS transaction User authenticated! (2532ms) established=true protReady=true srcName=apetrov@FNAL.GOV targName=daeset\/bd@foo.fnal.gov mutualAuthState=false integState=false confState=false replayDetState=false sequenceDetState=false lifetime=2147483647 Waiting for new client request...