Wallet Implementation Notes

Introduction

Collected here are implementation notes about design decisions, external interfaces, integration, internal structure, and related issues. This document will mostly be of interest to people who want to modify the wallet code or who are curious about its design.

This is now mostly of historical interest. For information about how the wallet now works, see the documentation for each of the server classes, the other design documents, and the man pages for the programs. Some portions of this document may be inaccurate or not reflect how the wallet system is currently implemented.

Server Issues

  Interface
    We need two interfaces for retrieving items, one which retrieves the
    current stored item and one which generates a new item.  This
    particularly applies to keytabs.  We also don't want new keytabs to be
    generated for certain keys even by accident without an explicit action
    taken, but for most keytabs we want to generate new keys each time.
    So we need an interface like:
    get keytab
      Generates a new keytab normally, but retrieves the existing keytab
      if we've marked the key as unchanging.
    flag set unchanging
    flag clear unchanging
      Change the state to generate new keytabs each time or always try to
      pull the existing key.  This operation should probably be
      privileged.
    So if you want to generate a new key for a keytab that would otherwise
    be persistant, mark it changing, download the new key, and then mark
    it unchanging again.
    Possibly need to do something about occasionally changing keys of
    keytabs that are otherwise marked unchanging, or we're going to open
    ourselves to brute force attacks.
  ACL Management
    Supported operations are: get, store, create (possibly triggered by a
    get or store of something that didn't already exist), destroy, show,
    and setting or clearing flags.  Each of these need a separate ACL
    potentially.  Not sure if we're going to need separate ACLs for each
    flag operation.
    Administrators get implicit access to do anything except get and
    store.  They can give themselves get and store access just by changing
    ACLs, of course, but it encourages sloppy ACLs to let them get and
    store without an explicit ACL (administrators then end up relying on
    that privilege and not reflecting it in ACLs).  There does need to be
    an ACL on create, but that should probably be implemented via some
    sort of policy callout.  The user will write a simple Perl function
    that returns a default ACL given the object type and name if the
    object doesn't already exist.
    Owner rights provides get, store, show, and destroy, but not setting
    or clearing flags.  This can be overridden by more precise ACL
    settings.  So the ACL logic would go like this:
     * If the user is an administrator and the operation isn't get or
       store, operation is permitted.
     * Otherwise, check the object.  If it exists and has a setting for
       that specific ACL, apply that ACL.
     * If the object exists but with no specific ACL setting and the
       operation is one of get, store, show, or destroy, apply the owner
       ACL.
     * If the object doesn't exist and the action is get, store, or
       create, punt to a local policy if it exists and see if it returns a
       default ACL.
    I think the owner abstraction is worth it over just setting the ACL
    for get, store, and show.
  Flags
    locked      --  No operations permitted except show
    unchanging  --  Pull existing value from file store
    For backends like secure files, all values are unchanging implicitly,
    but I don't think we should represent this by setting flags on every
    instance of those backends; it's just confusing and doesn't provide
    more information.  In other words, unchanging is only meaningful for
    backends that normally dynamically generate new keys on get, like the
    keytab backend.
  Expiration
    The database has a field to store an expiration date for every object.
    We can implement expiration methods in the backend to automatically
    destroy some objects (or perhaps lock them) when they pass their
    expiration date, but a more useful method might be to provide warnings
    when objects are about to expire via warning methods for a backend
    that take the object name and the expiration date.  This would be
    great for certificates, for instance.
    Not clear whether every get and store should check the expiration or
    just a nightly cron job that blows away or locks objects when they
    pass their expiration.
  Keytab Backend
    As of the deployment of the wallet, we want to stop limiting nearly
    all keytabs from being forced to single DES keys.  We're probably
    still going to have some keys for which only particular enctypes are
    permitted, however.  This means keeping a side table of allowable
    enctypes per keytab name, where if there are no entries in the table
    we allow any enctype.  We can pass a list of enctypes into kadmin when
    doing the principal creation or randomization, separated by spaces and
    enclosed in double quotes.
    When creating a new principal with addprinc, pass the -clearpolicy
    flag.  Otherwise, the principal will be placed in the default policy
    and will be subject to password strength checking, and the initial
    password used with -randkey will fail.
    Whenever we generate a new keytab, we may need to push the key into
    K4.  We could make the client send a flag saying whether they want
    synchronization with K4, but it's easier to just always do it (except
    maybe for some exception cases).  The user doesn't have to ask the
    client program for the srvtab if they don't want it, and it doesn't
    hurt to create the KDC entry.  We use kasetkey for this.
  Certificate Creation
    We probably want to handle all requested certificates from Comodo
    using this interface since we can use its expiration handling to do
    warnings and since that way users can re-download the certificate any
    time they want.  Certificates are actually pairs of certificate and
    key, though, and we need to figure out what we're storing.  There is
    the key, which we want to be able to store but we don't really do
    anything with (except ideally it's associated with a certificate),
    there's the CSR (which we could reuse for renewals although that
    doesn't get people to change their key), and there's the certificate
    itself (which is actually public data).
    People seem to like to have CSRs kept around, but I don't understand
    why and need to investigate this further.  It makes more sense to me
    to generate a new key every time the cert is renewed, for additional
    security.
    We may be able to just store a file that contains both the key and the
    public certificate and change our practices on web server
    configuration to point them at the combined file.
  Cleanup of Old Entries
    We should periodically scan the wallet for host-based entries for hosts
    that aren't in NetDB.  Rather than removing them immediately, wait
    until we haven't seen the host for several consecutive passes and then
    purge them.  Send notification of the hosts that are being purged (and
    maybe of the hosts that will be purged soon if nothing happens),
    although that raises the question of where to send the notification.

Client Issues

  Command-Line Options
    Some of the specific data types are going to need their own flags to
    operations like get.  As an example, the keytab get operation will
    need an optional flag to specify the srvtab file to which to also
    write the key, and will need an optional flag specifying the time
    delta at which old kvnos should be pruned from the keytab.  These
    flags need to be globally unique in the wallet client so that we can
    use a naive option parser.  It would also be nice to support the flags
    anywhere in the command and not go down the weird CVS route of flags
    meaning different things in different places.
  Keytab Handling
    The server is going to hand the client a keytab that contains the
    current keys for the given service.  Unless the keytab was marked as
    unchanging, these entries will have a higher kvno than any keys
    already in the keytab on the local system.
    The only interfaces to read keytabs require a file, so the client will
    need to save the keytab to a temporary file in order to extract
    individual keys.  If there is no keytab on the local system in the
    path given to the wallet, this is simple; just write the keytab as
    returned by the server into the file.
    If the keytab already exists, we want the following behavior:
     * Add the keys from the new keytab.
     * Retain in the keytab keys for the previous kvno, but not for any
       older kvno older than the maximum lifetime of Kerberos tickets.  So
       scan the keytab for keys with an older kvno and a timestamp older
       than one day (maybe make it a week just in case) and delete them.
       (Possibly make this configurable.)
     * Delete any keys in the keytab matching the current kvno, just to be
       sure we don't get any strange issues.
    We want to try to add the new keys first to minimize the outage window
    where service tickets handed out by the KDC aren't recognized by the
    host.  Adding the keys does just append them to the end, but we
    probably have to clean out any keys with the same kvno first.  That's
    a rare case, so I don't think we have to worry about the outage window
    there.
  Srvtab Handling
    If a srvtab was requested, we search for the key in the new keytab
    that has an enctype of ENCTYPE_DES_CBC_CRC and then write it out to a
    srvtab file.  The MIT Kerberos library doesn't support writable
    srvtabs in the keytab backend, so we roll that ourselves.
    Look at src/lib/krb5/keytab/kt_srvtab.c in the MIT Kerberos source for
    the format of a srvtab file (see the end of that file).
    The kvno that we get from K5 may have no bearing on the kvno in K4.
    In order to get the K4 kvno, use the new key to obtain a K4 service
    ticket for ourselves and then read the kvno off that service ticket.
    There are other approaches, but the other approaches all require
    changes to the server side as well, whereas this is self-contained in
    the client and can be more easily dropped when we drop K4.

License

    Copyright 2006-2008, 2013
        The Board of Trustees of the Leland Stanford Junior University
    Copying and distribution of this file, with or without modification,
    are permitted in any medium without royalty provided the copyright
    notice and this notice are preserved.  This file is offered as-is,
    without any warranty.
    SPDX-License-Identifier: FSFAP