                          PAM Testing Framework

Overview

    The files in this directory provide a shim PAM library that's used for
    testing and a test framework used to exercise a PAM module.

    This library and its include files define the minimum amount
    of the PAM module interface so that PAM modules can be tested without
    such problems as needing configuration files in /etc/pam.d or needing
    changes to the system configuration to run a testing PAM module
    instead of the normal system PAM modules.

    The goal of this library is that all PAM code should be able to be
    left unchanged and the code just linked with the fakepam library
    rather than the regular PAM library.  The testing code can then call
    pam_start and pam_end as defind in the fakepam/pam.h header file and
    inspect internal PAM state as needed.

    The library also provides an interface to exercise a PAM module via an
    interaction script, so that as much of the testing process as possible
    is moved into simple text files instead of C code.  That test script
    format supports specifying the PAM configuration, the PAM interfaces
    to run, the expected prompts and replies, and the expected log
    messages.  That interface is defined in fakepam/script.h.

Fake PAM Library

    Unfortunately, the standard PAM library for most operating systems
    does not provide a reasonable testing framework.  The primary problem
    is configuration: the PAM library usually hard-codes a configuration
    location such as /etc/pam.conf or /etc/pam.d/<application>.  But there
    are other problems as well, such as capturing logging rather than
    having it go to syslog and inspecting PAM internal state to make sure
    that it's updated properly by the module.

    This library implements some of the same API as the system PAM library
    and uses the system PAM library headers, but the underlying
    implementation does not call the system PAM library or dynamically
    load modules.  Instead, it's meant to be linked into a single
    executable along with the implementation of a PAM module.  It does not
    provide most of the application-level PAM interfaces (so one cannot
    link a PAM-using application against it), just the interfaces called
    by a module.  The caller of the library can then call the module API
    (such as pam_sm_authenticate) directly.

    All of the internal state maintained by the PAM library is made
    available to the test program linked with this library.  See
    fakepam/pam.h for the data structures.  This allows verification that
    the PAM module is setting the internal PAM state properly.

  User Handling

    In order to write good test suites, one often has to be able to
    authenticate as a variety of users, but PAM modules may expect the
    authenticating user to exist on the system.  The fakepam library
    provides a pam_modutil_getpwnam (if available) or a getpwnam
    implementation that returns information for a single user (and user
    unknown for everyone else).  To set the information for the one valid
    user, call the pam_set_pwd function and provide a struct passwd that
    will be returned by pam_modutil_getpwnam.

    Only those two functions are intercepted, so if the module looks up
    users in other ways, it may still bypass the fakepam library and look
    at system users.

  Output Handling

    The fakepam library intercepts the PAM functions that would normally
    log to syslog and instead accumulates the output in a static string
    variable.  To retrieve the logging output so far, call pam_output,
    which returns a struct of all the output strings up to that point and
    resets the accumulated output.

Scripted PAM Testing

    Also provided as part of the fakepam library is a test framework for
    testing PAM modules.  This test framework allows most of the testing
    process to be encapsulated in a text configuration file per test,
    rather than in a tedious set of checks and calls written in C.

  Test Script Basic Format

    Test scripts are composed of one or more sections.  Each section
    begins with:

        [<section>]

    starting in column 1, where <section> is the name of the section.  The
    valid section types and the format of their contents are described
    below.

    Several strings undergo %-escape expansion as mentioned below.  For
    any such string, the following escapes are supported:

        %i      Current UID (not the UID of the target user)
        %n      New password
        %p      Password
        %u      Username
        %0      extra[0]
        ...
        %9      extra[9]

    The PAM output (described below) also supports a special %* wildcard.
    All of these are set in the script_config struct.  Blank lines and
    lines starting with # are ignored.

  The [options] Section

    The [options] section contains the PAM configuration that will be
    passed to the module.  These are the options that are normally listed
    in the PAM configuration file after the name of the module.  The
    syntax of this section is one or more lines of the form:

        <group> = <options>

    where <group> is one of "account", "auth", "password", or "session".
    The options are space-delimited and may be either option names or
    option=value pairs.

  The [run] Section

    The [run] section specifies what PAM interfaces to call.  It consists
    of one or more lines in the format:

        <call> = <status>

    where <call> is the PAM call to make and <status> is the status code
    that it should return.  <call> is one of the PAM module interface
    functions without the leading "pam_sm_", so one of "acct_mgmt",
    "authenticate", "setcred", "chauthtok", "open_session", or
    "close_session".  The return status is one of the PAM constants
    defined for return status, such as PAM_IGNORE or PAM_SUCCESS.  The
    test framework will ensure that the PAM call returns the appropriate
    status.

    The <call> may be optionally followed by an open parentheses and then
    a list of flags separated by |, or syntactically:

        <call>(<flag>|<flag>|...) = <status>

    In this form, rather than passing a flags value of 0 to the PAM call,
    the test framework will pass the combination of the provided flags.
    The flags are PAM constants without the leading PAM_, so (for example)
    DELETE_CRED, ESTABLISH_CRED, REFRESH_CRED, or REINITIALIZE_CRED for
    the "setcred" call.

  The [output] Section

    The [output] section defines the logging output expected from the
    module.  It consists of zero or more lines in the format:

        <priority> <output>

    where <priority> is a syslog priority and <output> is the remaining
    output.  Valid values for <priority> are DEBUG, INFO, NOTICE, ERR, and
    CRIT.  <output> may contain spaces and undergoes %-escape expansion.
    <output> may also end in %*, in which case only the line up to %* is
    required to match and anything after that in the same output message
    is permitted.

    The replacement values are taken from the script_config struct passed
    as a parameter to run_script or run_script_dir.

    If the [output] section is missing entirely, the test framework will
    expect there to be no logging output from the PAM module.

    This defines the logging output, not the prompts returned through the
    conversation function.  For that, see the next section.

  The [prompts] Section

    The [prompts] section defines the prompts that the PAM module is
    expected to send via the conversation function, and the responses that
    the test harness will send back (if any).  This consists of zero or
    more lines in one of the following formats:

        <type> = <prompt>
        <type> = <prompt>|<response>

    The <type> is the style of prompt, chosen from "echo_off", "echo_on",
    "error_msg", and "info".  The <prompt> is the actual prompt sent and
    undergoes %-escape expansion.  The <response> if present (and its
    presence is signaled by the | character) contains the response sent
    back by the test framework and also undergoes %-escape expansion.

    If the [prompts] section is present and empty, the test harness will
    check that the PAM module does not send any prompts.  If the [prompts]
    section is absent entirely, the conversation function passed to the
    PAM module will be NULL.

License

    This file is part of the documentation of rra-c-util, which can be
    found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.

    Copyright 2011 Russ Allbery <rra@stanford.edu>

    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.
