Pluggable configuration design choices

ghudson@MIT.EDU ghudson at MIT.EDU
Wed Jun 29 18:01:59 EDT 2011

I'm interested in feedback on the following design areas for making
krb5 configuration pluggable.

1. Module selection and registration

For system-level control of the configuration source, we need some way
of determining which configuration source to use and where the shared
object for that module can be found.  Obviously, it cannot involve the
profile, so the usual method of module registration is out.

Some options are:

* A new environment variable
  (e.g. KRB5_CONFIG_SOURCE=module:residual).

* Overload KRB5_CONFIG (e.g. :module:residual or

* A new config file in a hardcoded location (or a new registry setting
  on Windows).

* The first line of krb5.conf specifies the module and residual
  somehow (perhaps using a magic comment line).

Using environment variables raises questions for how
krb5_init_secure_context() should behave.

For application-level control of the configuration source, I figure
the best choice is to bypass the plugin framework and just have the
application pass in a vtable.  I'm not yet sure of the exact choice of
API (we already have three variations on krb5_init_context() and I
don't really want to make it six).

A second question is whether "module" in the above is a pathname or a
module name.  If it's a module name, we would have to require module
shared objects to live in a specific place
(<libdir>/krb5/plugins/config/modname<suffix>).  Or we could allow
either, depending on whether there's a slash in the name.

2. Plugin interface

Based on past input, I'm assuming that we will need read and write
methods.  The core krb5 code doesn't need to write to configuration
sources, but the profile API allows applications to do so.  I'll just
talk about the read methods here, and assume that the write methods
mirror them (plus a "flush" method).  Of course write methods will be
optional to implement.

A configuration plugin will map keys to values.  Keys will probably be
represented as a null-terminated array of C strings
(e.g. {"libdefaults", "allow-weak-crypto", NULL}).  From there, some
options are:

* Minimal: Just have a single "get" method for mapping keys to a list
  of strings.

* Typed: Also have methods for mapping a key to a single string, or an
  integer, or a boolean value.

* Typed with fallback: if, say, get_int isn't implemented by the
  plugin, call the basic get and convert the first string value to an

* Typed with two-level fallback: fallback to get_string first, and
  then to the basic get method if get_string isn't implemented.

The advantage of the minimal interface is that it makes it easier to
implement pass-throughs.  I am guessing people would prefer a typed
interface, though.

Do we need iteration methods?  I don't think we do.

There will, of course, be a context-level initializer method which
takes a residual value,

3. KDC configuration

We have a special class of configuration consumers for programs which
run on the KDC (krb5kdc, kadmind, kdb5_util, etc.).  Right now they
merge a second kdc.conf file into the profile.  We'll need some
analagous behavior when a configuration plugin is in use.  An obvious
choice is a boolen argument to the plugin initializer saying whether
the consumer is a KDC-type program.

4. Consumer API

Currently, applications access krb5 configuration using
krb5_get_profile() and the profile API, or the somewhat broken
krb5_appdefault_string/krb5_appdefault_boolean functions.  krb5
library code accesses krb5 configuration using the profile API
directly on context->profile.

I think we can avoid changing any of that using the following design:
add a new initializer to libprofile which takes a vtable and module
data pointer instead of a list of paths.  When operating on a profile
object created that way, libprofile would pass API requests through to
the vtable.

More information about the krbdev mailing list