git clone 'https://github.com/fjames86/cerberus.git'
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.
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.
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.
(with-current-user ((logon-user "username@realm" "Pasword" :kdc-address "10.1.1.1"))
Services, which do not require initial authentication with the KDC, should use
(logon-service "service/host.name.com@realm" keylist)
KEYLIST is a list of keys as returned from either
Kerberos authentication is then performed using the GSSAPI as provided by the glass package.
;; ---------- client -------- CL-USER> (logon-user "username@realm" "password" :kdc-address "10.1.1.1") ;; acquire a client credential structure for the current user CL-USER> (defparameter *client-creds* (gss:acquire-credentials :kerberos "service/host.name.com@realm")) *CLIENT-CREDS* ;; initialize a context and generate a token buffer to send to the server CL-USER> (multiple-value-bind (context buffer) (gss:initialize-security-context *client-creds* :mutual t) (defvar *client-context* context) (defvar *buffer* buffer)) *BUFFER* ;; -------- on the server ----- CL-USER> (logon-service "service/host.name.com@realm" *keylist*) ;; acquire a crednetial structure for the current user CL-USER> (defparameter *server-creds* (gss:acquire-credentials :kerberos nil)) *SERVER-CREDS* ;; accept the context and generate a response token (if required) CL-USER> (multiple-value-bind (context buffer) (gss:accept-security-context *server-creds* *buffer*) (defvar *server-context* context) (defvar *response-buffer* buffer)) *RESPONSE-BUFFER* ;; -------- client ----------- ;; pass the token back to the client so it can validate the server CL-USER> (gss:initialize-security-context *client-context* :buffer *response-buffer*)
To discover the location of your KDC on the network, you should issue a DNS SRV query, e.g. using dragons:
CL-USER> (dragons:query (dragons:question "_kerberos._tcp.my.domain.com" :srv))
:RDATA (:PRIORITY 0 :WEIGHT 100 :PORT 88 :TARGET
:RDATA #(10 1 1 47)))
((:NAME "_kerberos._tcp.my.domain.com" :TYPE :SRV :CLASS :IN))
Cerberus supports a set of encryption “profiles”, which are implemented by specializing a set of generic functions.
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
Note: there currently is no way to use the contents of a keytab file.
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
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.
Add some SPNs and start the server:
CL-USER> (cerberus-kdc:add-spn "krbtgt/FRANK@FRANK" "mykdcpassword")
CL-USER> (cerberus-kdc:add-spn "frank@FRANK" "1234")
CL-USER> (cerberus-kdc:add-spn "dave@FRANK" "4321")
CL-USER> (cerberus-kdc:start-kdc-server "FRANK")
On the client, logon and get a ticket:
CL-USER> (cerberus:logon-user "frank@FRANK" "1234" :kdc-address "10.1.1.1")
CL-USER> (gss:acquire-credentials :kerberos "dave@FRANK")
The Cerberus KDC supports an RPC interface for configuration over the network. It is defined in kdc.x and implemented in kdc.lisp. Clients MUST be authenticated using AUTH-GSS:
CL-USER> (defparameter *client* (make-instance 'frpc:gss-client :credentials (gss:acquire-credentials :kerberos "krbtgt/FRANK@FRANK") :program 901980025 :version 1 :host "10.1.1.1")) CL-USER> (cerberus-kdc:call-find "frank@FRANK" :client *client*)
Licensed under the terms of the MIT license.
Frank James April 2015.