Monday, May 14, 2007

Nobody's in need of a re-entrant Kerberos 5, why?

IMHO, the Kerberos protocol is the solution for implementing Single-Sign-On (SSO), at least in the context of corporate intranets. Kerberos is practically the only solution available for SSO in a heterogenous computing environment comprised of different operating systems including UNIX variants and Windows. The only time a user ever has to provide a password is during the initial logon on a workstation. The credentials obtained can be passed to specific services and, more importantly, Kerberos allows delegation so that user credentials can be passed from the workstation via service A to service B, for example. Sure, several other SSO implementations exist but, to my knowledge, none of then can offer of the level of transparency and platform-coverage Kerberos can.

There's a catch however, Kerberos 5 implementations available for Linux / UNIX are not re-entrant and thus cannot be used in multi-threaded applications without serializing all code paths accessing the Kerberos API. The main Kerberos implementation I primarily refer to here is MIT Kerberos, the original implementation, Heimdal and friends shouldn't make any difference here however when it comes to a thread-safe implementation. Thinking about it, I haven't reviewed the Java GSSAPI source yet but my guess is that calls are serialized in the JNI part of the library to make sure that Java clients can safely use it across threads.

It might be hard to believe but, for once, Microsoft has managed to create something that is superior to Linux / UNIX equivalents (to be fair, there are other exceptions as well): The Microsoft Windows Kerberos implementation is fully re-entrant. Doesn't come as a surprise to me because Microsoft simply couldn't afford to provide an API that isn't - at last, Windows itself and practically all applications and services are inherently multi-threaded. In my opinion, that is exactly the reason why there's no re-entrant MIT Kerberos available on Linux / UNIX: Except for most GUI applications, common daemons are single-threaded and use a process model instead that often is fork(2) driven:
  • sendmail
  • pop3d / imapd variants
  • sshd
  • ntpd
  • Samba
  • PostgreSQL
  • Apache 1.3 and earlier
  • ...
So, in most cases, the lack of a re-entrant Kerberos implementation is not at all an issue. As a result, the current demand for it doesn't cause that issue to land on the project's TODO list. New implementations and more modern service implementations like Apache 2.0 and later (MPM = {worker|event}) and MySQL are fully multi-threaded, and, once Kerberos support comes into play, would have a problem or at least would need more time to implement Kerberos support (because of the locking business).

Granted, the number of installations dealing with multi-threaded applications (a small subset already) as well as Kerberos (in most cases, it's only an issue for medium to large-scale installations) is not exactly substantial - not substantial enough for supporting the refactoring of existing implementations towards re-entrance. This is a strong case of a minority being discriminated against, please spread the word to make a difference ;)

Seriously, in the Linux / UNIX world, there's currently no satisfactory solution available because even those saying that serialization solves it all are mistaken. Consider a large-scale Kerberos-enabled application using Apache as an interface to application users. In each application module, Kerberos may be used and calls must be synchronized. However, that's easier said than done because that synchronization must include all modules, it must span the whole process to solve this issue. If modules originate from more than one project or vendor, you've lost already - process-wide synchronization would require an agreement across all modules on the means of synchronization. If all modules are controlled by a single project or vendor and/or the source is available for those that are controlled by a different group, re-entrance can be established using a consistent locking protocol (e.g. by providing access to a single, process-wide mutex that is owned by a module all other modules have access to). Otherwise, there's practically no way to work around the lack of re-entrance (except for maybe preloading a library implementing Kerberos symbols that basically wraps all externals and serializes access or rolling your own release based on an open source Kerberos implementation).

You see, this dilemma is nerve-wracking because there's no satisfactory solution. Most likely, several more years will have to pass (where more multi-threaded daemons can emerge and require multi-threaded Kerberos implementations) until this problem will be solved with the availability of a re-entrant implementation...

How do you cope with Kerberos and thread-safety on Linux / UNIX platforms?

No comments: