1 Introduction

cerberus

A Common Lisp Kerberos (version 5) implementation.

This is an implementation of the Kerberos v5 authentication protocol in Common Lisp. The intention is to provide
a robust, reliable and portable (across both Lisp implementations and host OSs) Kerberos authentication system.
It has been developed/tested against the Windows KDC (i.e. active directory) running on SBCL under both Windows and Linux.

1. Introduction

Kerberos is the de facto standard method of authentication over a network, notably in Microsoft Windows environments.

The basic principal of Kerberos is there is a trusted central authority which stores credentials (password equivalents)
for each principal (user account). This is known as the Key Distribution Centre (KDC).
A client can prove its identity to an application server by requesting a message from the KDC
which is encrypted with the server's private key. Only the server (and the KDC) have the knowledge to decrypt this message,
the client itself does not. The client forwards this message to the server, who decrypts it and examines
the contents of the message. Inside it will be some proof (e.g. a recent timestamp) that the client is who they say they are.

In its simplest form, the Kerberos protocol consists of the following sequence of exchanges:

Client sends a message to authentication server (AS) component of the KDC requesting a ticket for the ticket-granting server (TGS).

The AS responds with a message encrypted with the client's private key, only the client can decrypt this message.

The client sends a request to the TGS for a ticket for the desired principal (application server).

The client sends this ticket to the application server using the relevant application protocol.

The application server validates the ticket and approves access to the client.

The details get more complicated, but that is the general idea.

2. Project aims

[x] Be able to encode/decode all the relevant DER-encoded ASN.1 messages

[x] Support a suffiently wide range of encryption profiles to be useful. In practise this means the ones supported by
Microsoft.

[x] Send AS-REQ messages to the KDC to get TGTs

[x] Send TGS-REQ messages to the KDC to get credentials for application servers

[x] Encode/decode AP-REQ messages to send to application servers

[x] Validate AP-REQ messages to authenticate clients

[x] Wrap AP-REQ messages with the KRB5 OID, as required for GSS

[x] Some sort of GSSAPI integration (sort of there, some polishing required)

[x] Write a KDC server

3. Usage

Users should first "logon" by providing credentials and IP address of the KDC (Domain controller):

(logon-user "username@realm" "password" :kdc-address "10.1.1.1")

This modifies the global *CURRENT-USER* variable. Alternatively you may rebind this variable
if you require a local change of user.

4. Encryption profiles

Cerberus supports a set of encryption "profiles", which are implemented by specializing a set of generic functions.

[x] The simple DES-based profiles are all implemented and appear to be working, DES-CBC-MD5, DES-CBC-MD4 and DES-CBC-CRC.

[x] The Microsoft profile RC4-HMAC is working correctly. RC4-HMAC-EXP has an unknown problem and is not working correctly.
It has temporarily been disabled.

[x] The triple-des profile, DES3-CBC-SHA1-KD, is implemented and looks like it's working.

[x] The AES128 and AES256 profiles are working correctly.

5. Keytab files

You can load keytab files (as output from other Kerberos implementations, such from ktpass utility) using

CL-USER> (cerberus:load-keytab "my.keytab")

This returns a list of KEYTAB-ENTRY structures, which include information about the principal as well as the
encryption key.

Note: there currently is no way to use the contents of a keytab file.

6. KDC

Cerberus now supports a simple KDC server. Each SPN (service principal name) is stored as an entry in a pounds
database. The krbtgt principal is mandatory because this is the principal under which the TGS runs. So you must
first run

CL-USER> (cerberus-kdc:add-spn "krbtgt/MYREALM@MYREALM" "password")

before it can be used. Of course, you will also want to add other principals for each user and service.

7. TODO

[ ] Need to be able to renew tickets (written the function but does it work?)

[x] Somehow need to be able to use this in an application that requires GSS support.

[x] Need to support encrypting application messages using the (sub)session key.

[ ] Some sort of credential cache, i.e. database of TGTs and tickets for other principals.

[ ] Support cross-realm requests and tickets.

[ ] Need to support sub-session keys. At the moment it is assumed only the session key is available.

[ ] A persistent credential cache? Could use the serializer to write the tickets out to a file.

[ ] Support forwarding/forwarded TGTs. This is required to fully enable Single-Sign-On semantics,
since it allows the application server to request tickets as if it were the client, effecitively forwarding the
client's identity to the application server.

[ ] It is impossible to know for sure what the correct salt to use is when requesting a TGT because it may not
be the default. We should therefore detect a KRB-ERROR-T (PREAUTH-FAILED) and inspect the data, this should
include an ETYPE-INFO or ETYPE-INFO2 structure which contains the real salt. Maybe we can just provide a suitable
restart?

8. Notes

Both the DER serializer and the encryption functions cons A LOT.

The ASN.1 serializer is specific to this project and NOT a generalized ASN.1 (DER) serializer. It makes certain assumptions which are valid
in the context of Kerberos messages, but are not generally applicable. Perhaps it could form the basis of one in the future.

This was developed and tested against the Windows KDC (i.e. active directory). It should work with other KDCs such as MIT and Heimdal,
but I've not tried. It is also internally consistent, so it works with the cerberus KDC as well.

Need to understand the MS-PAC structures, these contain authorization data that is likely to be very useful.

Need to think more clearly about how to properly handle credential caching. What's there at the moment is broken, I have disabled it for the moment. Really it should be persisted anyway (write them out to a file?).

Login the current user as a service principal. This function returns a login token to be bound to the *current-user*.
If there is no current user it will set *current-user* to the return value of this function. This function differs from
LOGON-USER because it does not contact the KDC, so no TGT is granted to the service. This means login tokens generated
using this function CANNOT be used to talk to other principals. Only use it when you know your service will be passively
authenticating clients.

PRINCIPAL ::= a string of form <principal>@<realm>.

PASSWORD-OR-KEYLIST ::= if a string, will be used as the password of the principal and passed to generate-keylist. If a
list then is interpreted to be a keylist, as returned from generate-keylist or load-keytab.

MODE ::= usage mode. If :interactive then always sets the *current-user*. If :network then never modifies *current-user*.

MODE ::= a symbol naming a logon mode, :INTERACTIVE implies modifying the *CURRENT-USER*, :NETWORK does not modify the environment.
If *CURRENT-USER* is nil, :INTERACTIVE is implied, otherwise :NETWORK is implied.
KDC-ADDRESS ::= IP of the KDC. This MUST be supplied in the first call.

Login to the authentication server to reqest a ticket for the Ticket-granting server. Returns a LOGIN-TOKEN
structure which should be used for requests for further tickets.

USERNAME ::= username of principal to login.
PASSWORD ::= the password to use.
REALM ::= the realm we are logging in to.

KDC-ADDRESS ::= the IP address of the KDC.
TILL-TIME ::= how long the ticket should be valid for, defaults to 6 weeks from present time.
ETYPE ::= encryption profile name to use for pre-authentication.

Decrypt the ticket and check its contents against the authenticator.
If the input is an opaque buffer, it is parsed into an AP-REQ strucutre.
Alternatively, the input may be a freshly parsed AP-REQ structure. The encrypted parts must still be encrypted,
they will be decrypted and examined by this function.

KEY ::= an encryption-key structure defining the key to use.
OCTETS ::= an octet array containing the plaintext to encrypt.
LOCAL-ADDRESS ::= a HOST-ADDRESS structure naming the local server that is sending the message.