authlib

Name

authlib -- Courier Authentication Library

Synopsis

authpam {command} [arg...]

authpwd {command} [arg...]

authshadow {command} [arg...]

authuserdb {command} [arg...]

authvchkpw {command} [arg...]

authcram {command} [arg...]

authmysql {command} [arg...]

authpgsql {command} [arg...]

authldap {command} [arg...]

authdaemon {command} [arg...]

authcustom {command} [arg...]

authdaemond [start | stop | restart]

DESCRIPTION

This library is used for two purposes:

1. Read an E-mail address that is supposed to be for a local account. Determine the local account's home directory, and system userid and groupid.

2. Read a login id and a password. If valid, determine the account's home directory, system userid, and groupid.

The term "authentication" is used in the following documentation to refer to either one of these two functions. The library contains several alternative authentication implementations, that may be selected at runtime.

authdaemon

authenticates through a background daemon proxy. This is now the installation default. Unless otherwise specified, the authdaemon module will always be installed. authdaemon is installed instead of the other authentication modules. Those modules are instead compiled into a separate executable program, authdaemond that is initialized at system start, and runs in the background. The authdaemon authentication module forwards the authentication requests to authdaemond, which forwards the authentication request to the real authentication module, and the result of the request is eventually returned back to the application. Because the real authentication process runs as a persistent background process, it is possible for the authentication process to open and hold permanent connections to the back-end authentication database (be it an LDAP directory, a MySQL or a PostgreSQL server), instead of connecting and disconnecting for every request. Obviously, this tremendously improves the authentication performance.

authpam

authenticates using the system's PAM library (pluggable authentication module). This is, essentially, a way to use existing PAM modules for authentication functionality. Note, however, that the authenticated account's home directory, userid and groupid are still read from the /etc/passwd file, since PAM functionality is limited to validating account passwords.

authpwd

authenticates from the /etc/passwd file.

authshadow

like authpwd except passwords are read from /etc/shadow.

authuserdb

authenticates against the userdb(8) database.

authvchkpw

supports existing vpopmail/vchkpw virtual domains.

authcram

authenticates against the userdb(8) database using the challenge/response authentication mechanism (CRAM), instead of the traditional userid/password.

authmysql

authenticates against a list of mail accounts stored in an external MySQL database. The /usr/lib/courier-imap/etc/authmysqlrc configuration file defines the particular details regarding the MySQL database and the schema of the mail account list table.

authpgsql

authenticates against a list of mail accounts stored in an external PostgreSQL database. The authpgsqlrc configuration file defines the particular details regarding the PostgreSQL database and the schema of the mail account list table.

authldap

authenticates against a list of mail accounts stored in an external LDAP directory. The /usr/lib/courier-imap/etc/authldaprc configuration file defines the particular details regarding the LDAP directory layout.

authcustom

this is a stub where custom authentication code can be added. This authentication module is just a stub that doesn't really do anything. It's purpose is to serve as a placeholder where custom authentication code can be easily added.

This is a complete list of available authentication modules. The actual installed authentication modules are determined by the resources on the server. For example, the authmysql authentication module will be installed only if the system provides MySQL support libraries.

authdaemon authentication module

The following command must be executed from the system startup script in order for the authdaemon module to work (and remember that authdaemon is installed by default:

/usr/lib/courier-imap/libexec/authlib/authdaemond start

"authdaemond stop" should also be added to the system shutdown script.

The /var/run/authdaemon.courier-imap subdirectory must be created in advance. This directory will have the filesystem socket used for interprocess communication between authdaemon and authdaemond. It goes without saying that the underlying filesystem for /var/run/authdaemon.courier-imap must support filesystem domain sockets. This pretty much excludes all network filesystems, so this directory should reside on a local disk.

/var/run/authdaemon.courier-imap MUST NOT HAVE any world-readable, executable or writable permissions! Under NO circumstances should this be allowed to happen. The exact permissions and ownership of /var/run/authdaemon.courier-imap varies. For the standalone versions of Courier-IMAP and SqWebMail, /var/run/authdaemon.courier-imap should be owned by root, and have no group or world permissions. For the Courier mail server, /var/run/authdaemon.courier-imap should be owned by the userid that Courier is installed under, and it must be readable and writable by the Courier user and group (but no world permissions).

Configuring authdaemond

The /usr/lib/courier-imap/etc/authdaemonrc configuration file sets several operational parameters for the authdaemond process. See the comments in the default file installed for more information. Currently, /usr/lib/courier-imap/etc/authdaemonrc sets two parameters: number of daemon processes, and authentication modules/process that will be used.

Although authdaemond might include several authentication modules, not all of them may be used. This makes it possible to install the same authdaemond build on multiple systems with different authentication needs. The default module list specified in /usr/lib/courier-imap/etc/authdaemonrc would be a list of all the available authentication modules.

/usr/lib/courier-imap/libexec/authlib/authdaemond is actually a very short shell script that executes the real authdaemond program. The available programs are authdaemond.plain, authdaemond.ldap, authdaemond.mysql, and authdaemond.pgsql. The "plain" program contains all the authentication modules except for authldap, authmysql, and authpgsql. The "ldap" program includes all the authentication modules in "plain", plus authldap Ditto for the "mysql" and "pgsql" processes.

This arrangement allows convenient creation of pre-configured binary packages. The authdaemond shell script runs authdaemond.plain only if none of the other processes are installed on the system. First, it checks if authdaemond.ldap, authdaemond.mysql, or authdaemond.pgsql is installed. If not, authdaemond.plain is brought up. This makes it possible to prepare a basic binary package that provides only basic authentication services and does not require either LDAP, MySQL, or PostgreSQL runtime support on the server. If either of these authentication requirements are needed, a separate binary sub-package will load the appropriate authdaemond process.

Note that it is not possible to use both LDAP and MySQL, for example, authentication at the same time. That's because their support is in different authdaemond processes, and only one authdaemond process can run at the same time. If both (or all three non-plain processes) are installed, the authdaemond script picks either the first one it finds, or whatever is explicitly specified in the /usr/lib/courier-imap/etc/authdaemonrc configuration file.

The number of authdaemond processes is also set in this configuration file. The more processes that are started, the more authentication requests can be handled. If authdaemon does not receive an answer within a moderate amount of time, it will declare an authentication failure, and abort. Try increasing the number of processes if you start seeing random authentication failures. However, that should only be used as a stop-gap measure. If the default number of authdaemond processes proves to be insufficient, it is far more likely that more resources are needed for the server: more RAM, a faster disk, or a faster CPU, at least in the humble opinion of the author. Increasing the number of processes should only be used as a stop-gap measure, until a more thorough analysis on the bottleneck can be made.

/usr/lib/courier-imap/libexec/authlib/authdaemond restart

Run the above command after making any changes to /usr/lib/courier-imap/etc/authdaemonrc. "authdaemond restart" is required for any changes to take effect.

AUTHENTICATION PROTOCOL

The rest of this documentation describes the internal protocol used by this authentication library. It is only of interest to developers who wish to extend the authentication library to support a custom authentication module, or a derived extension to an existing module.

The original implementation of this authentication library used small, self-contained, binary programs, named for their authentication module: authldap, authpam, and others. Later, the authdaemon module came about, which wrapped the other authentication modules into a separate background daemon process, which communicated with the authdaemon module. The authdaemon module is now always enabled by default, but it is still possible to build and install each authentication module as a self-contained binary program. Note, however, that applications such as SqWebMail, and Courier link directly with all the authentication modules, and will not use external modules for authentication.

authdaemon came about as a direct result of technical issues that prevented SqWebMail and Courier from using external binary modules. authdaemond is really nothing more than a simple application that links directly with the authentication modules, and talks to the authdaemon authentication module that follows this authentication protocol.

Stand-alone authentication modules

This section describes the authentication protocol for self-contained authentication modules. Although the default configuration no longer uses self-contained modules, the stand-alone protocol serves as a foundation for the protocol used by authentication modules as part of the authdaemond authentication process, or when they are linked directly with the application that uses this authentication library.

Here's the typical way that stand-alone authentication modules are used:

1. A list of authentication modules are read from a configuration file. Multiple authentication modules could be available, but not all of them are required in most situations.

2. The application executes the following command:

LOGIN AUTHMODULE1 AUTHMODULE2 ... APP

LOGIN is a full pathname to an application that reads the authentication information, such as the userid and a password. Arguments to LOGIN are full pathnames to each authentication module (if there are more than one), followed by a full pathname to the main application.

3. LOGIN reads the userid and password, then runs the program specified by its first argument, which is the first authentication module. The remaining arguments are passed to the new process. The mechanism by which the authentication information is passed to the authentication module is described later.

4. Each authentication module reads the authentication information, and determines if the previous authentication module succesfully processed the authentication request. If not, the module attempts to authenticate it itself. In any event, the module runs the next program specified by its first argument, and the remaining arguments are passed along to the next program. If any previous authentication module succesfully processed the authentication request, the next program is run immediately without any further processing.

5. Eventually, APP runs, APP reads the authentication information and determines whether any authentication module managed to succesfully process the authentication request. If so, APP runs normally. Otherwise, APP runs LOGIN with its original arguments in order to return an error message ("Password invalid", or something similar) and read the next authentication request.

Daisy-chaining authentication modules, in this fashion, makes it possible to have hybrid systems that use multiple authentication modules. Example: using authpam to authenticate system accounts, and authmysql to authentication virtual mailboxes without a corresponding system account.

Here's a more detailed description of the overall process:

The LOGIN process

The LOGIN process checks if the AUTHARGC environment variable is set. If not, this is the first time the LOGIN process comes up, and LOGIN displays the initial login prompt. Additionally, the command line arguments to LOGIN are literal copied to the AUTHARGC and AUTHARGVn environment variables. That is: the number of command line arguments is saved in AUTHARGC; the zeroth command line argument is saved in AUTHARGV0; the remaining command line arguments are saved in AUTHARGV1, AUTHARGV2, and so on.

After obtaining the authentication information (such as the userid and password), LOGIN creates a pipe, and arranges for the output end of the pipe to be located on file descriptor #3. The LOGIN process forks; the original process then executes the first authentication module, in the manner described earlier; the child process writes the authentication record to the pipe, then terminates.

The authentication record is a chunk of data in the following format:

SERVICE<NEWLINE>AUTHTYPE<NEWLINE>AUTHDATA

Each occurence of <NEWLINE> represents an ASCII linefeed character (#10). "SERVICE" identifies the service that originates the authentication request, such as "imap", or "webmail". Authentication module may use this identifier, or ignore it.

"AUTHTYPE" identifies the format of the authentication request. Everything after the second <NEWLINE> is an opaque blob of data, whose format is determined by AUTHTYPE.

Note

There's a theoretical upper limit on the maximum size of the authentication record. It is high enough not to matter in most situations (the total number of characters allowed cannot be more than 8189 characters on a typical GNU/Linux system).

The following AUTHTYPE formats are currently defined:

login

A typical userid/password authentication request. AUTHDATA contains the following data: userid<NEWLINE>password<NEWLINE>.

cram-md5

Specifies the CRAM-MD5 authentication request. AUTHDATA contains the following data: challenge<NEWLINE>response<NEWLINE>. The "challenge" and "response" strings are base64-encoded.

cram-sha1

Specifies the CRAM-SHA1 authentication request, instead of CRAM-MD5, and uses the same format for AUTHDATA.

The authentication module

The first thing an authentication module does is check if the environment variable AUTHENTICATED is set to a non-empty string. If so, it means that a previous authentication module has handled the authentication request, so this module simply runs the next program, specified by the first argument to this authentication module.

Otherwise, the authentication module reads the authentication record from file descriptor #3, and determines whether it wants to try this authentication record. If not, the module creates a new pipe, arranges the output of the pipe to be on file descriptor #3, forks, the parent process runs the next authentication module, and the child process writes the authentication record to the pipe, then exits.

There are two ways to handle an authentication request: 1) Use the AUTHARGC and AUTHARGVn variables to restart the entire authentication process - this is used in the event it is determined that the authentication request must be failed, or 2) run the next daisy-changed module, in the manner described previously, when it is determined that another authentication module can attempt to try to handle this request.

The following action occurs when the authentication module succesfully validates an authentication request:

1. The authenticated login ID is saved in the AUTHENTICATED environment variable.

2. The process's userid and groupid are reset to the corresponding userid and groupid of the authenticated login id, and the current directory is set to the process's defined home directory.

3. Some additional environment variables may also be initialized: AUTHFULLNAME - the login ID's full name; MAILDIR - the login ID's default maildir mailbox; MAILDIRQUOTA - the requested maildir quota.

The application process

Eventually, APP runs. The process closes file descriptor #3 (if it's open, and ignores the error if file descriptor #3 does not exist). If the AUTHENTICATED environment variable is set, it must mean that an authentication module was able to handle this authentication request, so APP starts up and runs normally. Otherwise the original command is reconstructed from the AUTHARGC and AUTHARGVn variables, and the initial login process runs again.

Library functions

This authentication library provides several convenient functions which can be used to quickly create a compliant login process, and its corresponding application. The login process should be structured as follows:

Example 1. A sample LOGIN process

int main(int argc, char **argv)
{
   if (authmoduser(argc, argv, TIMEOUT, ERR_TIMEOUT))
   {
      /* Print initial greeting here */
   }
   else
   {
      /* Error: invalid userid/password */
   }

   /* read userid and password */

   authmod(argc-1, argv+1, SERVICE, AUTHTYPE, AUTHDATA);
}

The authmoduser function takes care of copying the command line parameters to their corresponding environment variables, and checking whether or not this is the initial time this process runs, or if it is running again after a failed authentication process. TIMEOUT specifies the absolute login timeout, authmoduser quietly terminates the process if it runs due to a failed authentication request and at least TIMEOUT seconds have elapsed since the first time authmoduser was run. ERR_TIMEOUT specifies the number of seconds that authmoduser will sleep after a failed authentication request.

The SERVICE, AUTHTYPE, and AUTHDATA arguments to authmod are null-terminated character strings that form the authentication request. authmod takes care of setting up the pipe to the first authentication module, and runs it.

The application process is even simpler:

Example 2. A sample APP process

int main(int argc, char **argv)
{
const char *loginid=authmodclient();

   /* Application begins normally */

}

The authmodclient function returns the authenticated login ID. If the authentication request failed, authmodclient reruns the original login process, and doesn't return.

Inside an authentication modules

An authentication module needs to define the following structure:

Example 3. struct authstaticinfo

struct authstaticinfo {
        const char *auth_name;
        char * (*auth_func)(const char *, const char *, char *, int,
                        void (*)(struct authinfo *, void *),
                        void *);
        int (*auth_prefunc)(const char *, const char *,
                        int (*)(struct authinfo *, void *),
                        void *);
        void (*auth_cleanupfunc)();
        int (*auth_changepwd)(const char *, /* service */
                              const char *, /* userid */
                              const char *, /* oldpassword */
                              const char *); /* new password */
        } ;

auth_func points to a function that handles the authentication request. If succesful, auth_func is responsible for resetting the userid and groupid, changing to the authentication account's home directory, and setting up the necessary environment variables. The first three arguments to auth_func will be SERVICE, AUTHTYPE, and AUTHDATA. The next argument is a boolean flag which is non-zero if the authentication code is being called in the context of a stand-alone authentication module, or zero if the authentication code is called directly by an application. The fifth argument points is a callback function pointer, which may be NULL. If it's not null, auth_func should not reset the userid, groupid, or the home directory of this process, but should instead initialize the authinfo structure, which is defined as follows:

Example 4. struct authinfo

struct authinfo {
        const char *sysusername;
        const uid_t *sysuserid;
        gid_t sysgroupid;
        const char *homedir;

        const char *address;	/* The E-mail address */
        const char *fullname;	/* gecos, etc... */
        const char *maildir;
        const char *quota;

        const char *passwd;
        const char *clearpasswd;        /* For authldap */

        unsigned staticindex;   /* When statically-linked functions are
                                ** called, this holds the index of the
                                ** authentication module in authstaticlist */

        } ;

The passwd, clearpasswd, and staticindex fields are not used by auth_func. Either sysusername or sysuserid must be a non-NULL pointer. sysuserid specifies an explicit userid, otherwise sysusername is looked up in the password file.

The last argument to auth_func is an opaque pointer that gets passed as the second argument to the callback function.

auth_func should return a pointer to the authenticated loginid, in dynamic memory (the memory should be free()ed after user. A NULL return indicates an authentication failure. The authentication module should set errno to EPERM in the event that it the next authentication module should have a chance to process the authentication request, or use any other errno value to immediately fail the authentication request, and rerun the original login process.

Linked authentication modules

The auth_prefunc, auth_cleanupfunc, and auth_changepwd functions are not used by stand-alone modules, but when the authentication module is directly linked with an application.

auth_prefunc verifies that the requested userid exists. No passwords are validated, the first two arguments to auth_prefunc are the userid, and SERVICE. auth_prefunc should initialize an authinfo structure, and run the callback function, the third argument to auth_prefunc. The callback function receives the fourth argument to auth_prefunc as an opaque pointer.

auth_prefunc should come back with the callback function's return code, if the requested userid was found. Otherwise, auth_prefunc should return a non-zero integer. A positive integer should be return in the event that this authentication request should be stopped, and a negative itneger if another authentication module can be tried. An application that links against this authentication library will run each configured authentication module's auth_prefunc, until some module is able to process the requested userid, or until auth_prefunc comes back with a non-zero positive return code.

auth_func or auth_prefunc might allocate some internal resources, which should be freed by calling auth_cleanupfunc.

The auth_changepwd function is called to implement the change password functionality.

Other authentication library function

This authentication library contains several functions and macros that can be helpful in building authentication modules.

Example 5. Turning auth_func into a module

#define MODULE  auth_func
#include        "mod.h"

mod.h contains template code that reads an authentication request from the previous authentication module, call auth_func, in such a manner, and appropriately run the next module in the authentication chain.

The auth.h header file also declares several useful functions that authentication-related code may find convenient.

Building authdaemond

This authentication library builds alternate versions of the authdaemond background process. Some authentication modules have dependencies on external libraries, such as authldap, authmysql, and authpgsql. The authentication library prepares separate versions of authdaemond for each authentication module with a dependency. Each one of these authdaemond versions will also include all other authentication modules that do not have dependencies.

The authentication module configuration for each authdaemond is set in the authdaemond.versions file. A new authentication module will have to be added to authdaemond.versions (potentially creating another authdaemond build). The configure script must be run after making any changes to authdaemond.versions.

FILES

/usr/lib/courier-imap/etc/authmodulelist - list of authentication modules read by applications that directly link with authlib

/usr/lib/courier-imap/etc/authdaemonrc - authdaemond configuration file

/usr/lib/courier-imap/etc/authldaprc - authldap configuration file

/usr/lib/courier-imap/etc/authmysqlrc - authmysql configuration file

/usr/lib/courier-imap/etc/authpgsqlrc - authpgsql configuration file

SEE ALSO

courier(8), userdb(8)