authlib - Authentication module library

SYNOPSIS

   authpam program arg1 arg2...
   authpwd program arg1 arg2...
   authshadow program arg1 arg2...
   authuserdb program arg1 arg2...
   authvchkpw program arg1 arg2...
   authcram program arg1 arg2...
   authmysql program arg1 arg2...
   authldap program arg1 arg2...

DESCRIPTION

These modules read an account name and password from an external application, then return an indication to the external application whether or not the account name and password is valid. These modules cannot be normally executed from the command line because they expect to be executed by a separate application, which provides authentication information via environment variables and specially prepared pipes.

These authentication modules are usually installed automatically by an application that uses them. Not every one of these modules will be installed. Different modules implement different ways of authenticating the account name and password. Only the modules that are compatible with your system will be installed by default.

authpwd validates the account name and password against your /etc/passwd file.

authshadow validates the account name and password against your /etc/shadow file.

authuserdb validates the account name and password against your userdb(8) database.

authcram also uses userdb(8), but implements a challenge/response authentication mechanism (CRAM), instead of the traditional userid/password.

authpam validates the account name and password using your Pluggable Authentication Module (PAM) library. authpam allows PAM modules to be used to authenticate passwords by any application that uses this authentication library.

authvchkpw supports existing vchkpw-based virtual domain setups. New installations should use the userdb(8) database.

authldap is an experimental module that uses LDAP for authentication. authldap will authenticate clear-text or crypt-ed passwords in an LDAP database. If clear-text passwords are used, authldap will also be able to handle CRAM authentication, if the application that uses this authentication library supports CRAM authentication. See below for more information.

authmysql is another experimental module that authenticates against a MySQL table. See below for more information.

As previously mentioned, only those modules that are appropriate to your particular system will be installed. The authpam module will be installed only if your system supports PAM authentication. authldap will be installed only if LDAP client libraries are available. authpwd and authshadow will not be installed if you have PAM authentication or LDAP authentication. authvchkpw is usually installed only if your system has the vpopmail/vchkpw account. authuserdb is usually installed in any case. authcram is installed only if the application that uses this authentication library supports CRAM authentication. authmysql is usually installed if MySQL client libraries are found, and authvchkpw is not installed.

AUTHLDAP authentication module

This section applies if you have the authldap authentication module installed. This is an experimental authentication module, and may not be stable.

The authldap authentication module reads a configuration file. The configuration file tells authldap where to obtain the information that it needs. A default configuration file will be installed in ${sysconfdir}/courier-imap.authldaprc.

It will be necessary to edit this file in order to properly configure authldap. This configuration file contains a list of settings, one per line. Each line starts with the name of the setting, followed by one or more whitespace characters, followed by the setting value. Leading or trailing whitespace is not allowed.

Comments in the default configuration file describe what each setting does. LDAP_SERVER, LDAP_PORT, LDAP_BASEDN, LDAP_BINDDN, and LDAP_BINDPW specify your LDAP server's coordinates. LDAP_TIMEOUT sets the timeout for the LDAP query.

The LDAP query is keyed by the E-mail address. The LDAP_MAIL settings sets the LDAP attribute that is searched for, which should be "mail". The E-mail address should be of the form user@domain. If the domain is missing, authldap appends LDAP_DOMAIN to the query.

authldap will need to obtain the following attributes: the account's home directory, user and group ids, and the account password. These attributes are required. The password can be a clear-text password, or a crypt-ed password.

There are also several optional attributes which, if present, will be used: the full name of the user, and the location of the user's main mailbox. If the mailbox location is not specified, the default location, as defined by the system administrator, will be used.

The configuration file lists which LDAP attributes contain which properties. If all your accounts share the same global user and group IDs, which may be the case if you are authenticating access to a large number of virtual accounts, you can set the global user and group ID with the LDAP_GLOB_UID and LDAP_GLOB_GID setting. Otherwise, you must define LDAP_UID and LDAP_GID to be the LDAP attributes that contain the numerical user and group IDs.

LDAP_HOMEDIR specifies the name of the required LDAP attribute which contains the account's home directory, LDAP_MAILDIR specifies the name of the optional attribute that specifies the location of the default mailbox. If your virtual accounts have no home directory, set both LDAP_HOMEDIR and LDAP_MAILDIR to the same LDAP attribute that specifies the location of the virtual account's mailbox.

LDAP_FULLNAME is an optional LDAP attribute. Not every application that uses this authentication library will need it.

You must define either LDAP_CLEARPW and LDAP_CRYPTPW. It is possible to define both of them, but, in practice, only one of them needs to be defined. If you store clear-text passwords, set LDAP_CLEARPW to the name of the LDAP attribute that holds the clear-text password. If you store crypt-ed passwords in LDAP, set LDAP_CRYPTPW to the name of the LDAP attribute that holds the crypt-ed password.

If you store clear-text passwords, authldap will be able to carry out CRAM authentication. Only userid/password authentication is possible if crypt-ed password are used.

AUTHMYSQL authentication module

This section applies if you have the authmysql authentication module installed. This is an experimental authentication module, and may not be stable.

authmysql functions like authldap, but uses a MySQL The authmysql authentication module reads a configuration file. The configuration file tells authmysql where to obtain the information that it needs. A default configuration file will be installed in ${sysconfdir}/courier-imap.authmysqlrc.

It will be necessary to edit this file in order to properly configure authmysql. This configuration file contains a list of settings, one per line. Each line starts with the name of the setting, followed by one or more whitespace characters, followed by the setting value. Leading or trailing whitespace is not allowed.

Comments in the default configuration file describe what each setting does. Consult the configuration file for more information.

CONFIGURING AUTHENTICATION

This authentication library is used by applications that typically consist of two separate parts. The first part waits for the user to enter the account name and password. Then, it runs one or more authentication modules, which check if the account name and password is valid. The second part of the application is then executed. If the password is valid, the second part of the application runs normally. Otherwise, the first part is executed again, to try to read another account name and password.

This manual page provides generic instructions for using this authentication library. You will need to use the documentation that comes with each application to determine the filenames corresponding to the first, login, part and the second, application, part. The following example assumes that the application consists of the following programs: /usr/bin/getlogin and /usr/bin/app. getlogin reads the account name and password, and app is the actual application.

The following command starts this application with the authuserdb and authshadow modules:

/usr/bin/getlogin /usr/lib/authuserdb \
                  /usr/lib/authshadow \
                  /usr/bin/app

After obtaining the account name and password, getlogin simply runs the program specified by its first argument, which is authuserdb. The remaining command line arguments to getlogin are received as arguments to authuserdb. Note that this is not a pipe, there is no | here. authuserdb receives the remaining arguments as its own arguments, checks the account name and password, then runs the program specified by the first argument, which is authshadow. authshadow receives the remaining argument, checks the account name and password, if necessary, then runs the program specified by its first argument.

The protocol implemented by this authentication library permits allows multiple modules to try to authenticate the account name and password. Essentially, to start an application that uses this authentication library, you run its login part, then pass the pathnames to the authentication modules to be used as the arguments to the login part of the application. The pathname to the application part is usually the last argument. Where any module requires an option or an argument, the option or the argument is provided at the appropriate point on the command line.

Any application that uses this authentication library can use any combination of authentication modules that are installed, unless there are specific restrictions mention in the documentation for each application. All pathnames must be absolute. Pathnames relative to the current directory are prohibited.

Each authentication module runs the program specified by its first argument, after checking the account name and password. So, in the example given previous /usr/bin/app will be eventually executed by the last authentication module. Each authentication module checks if the account name and password has already been validated by a previous authentication module. If not, it tries to validate the account name and password by itself. The final module, the application part itself, checks if any authentication modules successfully validated the account name and password. If not, it will immediately execute the login part of the application using the same arguments that were originally used, and the process will begin again. Otherwise, if the account password was successfully accepted by any authentication module, the application part runs normally.

The login portion of the application, getlogin in this example, must be executed by the superuser. After the account password is successfully validated, the authentication module sets its user and group IDs of the process to the user group IDs for that account, and sets its home directory to the account's home directory. However, if all authentication modules rejected the account name and password, the application module, app in this example, will still be a superuser process when it starts. That's fine, because the first thing it does is check if the authentication succeeded, and, if not, it will run the login process again. Obviously, you can't use just any application with this authentication library. An application must be specially written to understand and use this protocol.

NETWORK SERVICES

Applications that use this authentication library are usually network services, and must be started in response to a network connection. There are several ways to do that. One way is to use the inetd(8) daemon. Here is a sample entry in /etc/inetd.conf for a hypothetical IMAP server that uses this authentication library:

imap stream tcp nowait root /usr/bin/imaplogin /usr/bin/imaplogin
/usr/lib/authpam /usr/bin/imapserver

This should all be entered on one line in /etc/inetd.conf.

Note that some inetd implementations automatically infer the argv[0] argument from the filename. You can use those inetd servers by removing the second /usr/bin/imaplogin entry (in this particular case) ONLY if the inetd server does NOT strip the pathname from theimplied argv[0] argument. The application login process (imaplogin in this case), MUST know its complete pathname so that it can be executed again if the first password is rejected.

tcpserver

If you use Dan Bernstein's tcpserver, here's an equivalent tcpserver startup command for this hypothetical IMAP application:

/usr/local/bin/tcpserver -H -R 0 imap \
    /usr/bin/imaplogin /usr/lib/authpam /usr/bin/imapserver & 

couriertcpd

If you use the couriertcpd server, here's an equivalent couriertcpd startup command for this hypothetical IMAP application:

/usr/local/bin/couriertcpd -nodnslookup -noidentlookup imap \
   /usr/bin/imaplogin /usr/lib/authpam /usr/bin/imapserver & 

You may specify appropriate options for tcpserver and couriertcpd that are right for you.

AUTHENTICATION PROTOCOL

The remainder of this document defines the protocol used by these authentication modules.

The overall application that uses this authentication library is made up of at least three processes:

The first process, called a "login process", is started as a superuser process in response to a network connection, or an equivalent event. It reads the userid and password, then starts an authentication module and securely transmits this information to the authentication module.

The second process is an "authentication module" that attempts to validate the userid and password in some arbitrary fashion. Whether or not the password is authenticated, the authenticated module runs the next process. That's because the next process can be another authentication module, or the authenticated client process (see below). If one authentication module fails, another authentication module can be given a chance to authenticate the userid and password. An authentication module can also decide that other authentication modules can not be used to attempt to authenticate this userid and password, because it should be unequivocally rejected.

If the authentication module successfully validates the userid and password, it sets the process's user and group ID to the authenticated user's, then sets the current directory to the authenticated user's home directory. The authentication module may also set some additional environment variables.

The final process is called the "authenticated client" process in this documentation. Contrary to its name, it is possible that it will be called even if all modules rejected the userid and password, so the first thing it does is check whether that's the case.

If any authentication module permanently rejects a userid and password, or if the authenticated client detects that all authenticated modules rejected the userid and password, the original login process is executed again, in order to try to read another password and start the process again.

It is expected that the login process is initially started by a daemon process, and it begins with a relatively clean environment. Modules use environment variables and pipes in order to implement this authentication protocol. When the login process starts it can check these environment variables to determine if this is the first time it is run, or if it is executed again in order to read another userid and password, after the first pair was rejected.

AUTHENTICATION MODULES

The login process is usually invoked by some system daemon process in response to a network connection. argv[0] to the login process must be a complete pathname. Otherwise, the AUTHUSER environment variable must be set prior to running the login process, and it must contain a complete pathname. It is possible that certain systems might strip the full path from argv[0], leaving just the filename, so use AUTHUSER in that case

Any options for the login process can be specified on the command line.

The first argument after any options must be a full pathname to the first authentication module to run.

It is possible to run two authentication modules, as previously described. The complete pathname of the second module should be the next argument. Additional authentication modules may also be specified in this fashion.

The arguments to the login process end with a complete pathname to the authenticated client process, followed by any options it requires. Here is a hypothetical example of a complete invocation of the login process:

  /usr/local/bin/readuserpass -d \
     /usr/lib/authuserdb \
     /usr/lib/authpam \
     /usr/local/bin/dostuff -x

This example runs /usr/local/bin/readuserpass to read the userid and password. This process uses the -d option. This is followed by two modules - authuserdb and authpam - they are specified as the modules which will be used to authenticate the userid and password. /usr/local/bin/dostuff -x is specified as the command to execute upon successful userid/password validation.

AUTHENTICATION LIBRARY

This and the following sections provide documentation that's of interest only if you want to write your own authentication modules. The low-level authentication protocol is defined later. The libauth.a and libauthmod.a libraries contain functions that handle all the low level details, and provide convenient high level authentication services that you can use to write your own authentication modules in C or C++.

Under normal conditions, the login process reads its command line arguments, and processes any options it knows about. Afterwards, the remaining command line arguments specify the next process to run.

The typical logic used by the login process is as follows:

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

   /* read userid and password */

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

This example does not use any options for the login process itself. If the login process parses any options, they must be removed from the argc/argv arguments to the call to the authmod function. For example, if the authentication user process received two options, subtract two from argc, and add two to argv to skip over them.

The TIMEOUT argument specifies the authentication timeout, in seconds. The authmoduser function calls alarm to set up an alarm signal to be delivered after the specified period of time (which will terminate this process by default).

The authmoduser function copies the command line arguments into environment variables. This allows the login process to be invoked with the same exact command line arguments in the event that the subsequent account password is rejected, in order to start the process again.

authmoduser checks if the environment variables are not already set, meaning that this is the first time the login process is executed. If so, it copies the command line arguments to environment variables, and returns non-zero.

If the environment variables were already set, the function returns 0 in this case after sleeping for the amount of time specified by the ERR_TIMEOUT argument (in seconds). Note that the TIMEOUT time interval will generally be a cumulative value. authmoduser checks the system clock, and subsequent invocation of the login process will automatically cause authmoduser to subtract the time already used up during the initial invocation of the process.

The return value from authmoduser can be used to determine whether a "password rejected" type of a message should be printed, or if a greeting message should be used instead.

Afterwards, the userid and password are obtained, in some fashion, and the authmod function is called. The first two arguments to authmod must be exactly as shown. If authmoduser received argc and argv, authmod must receive argc-1 and argv+1. If authmoduser received any options, argc/argv must be advanced appropriately. The next argument specifies the first authentication module to execute, which is precisely what the authmod function does.

SERVICE is a character string that specifies an "authentication service". This is not used by all authentication modules. The authpam module passes this directly to the PAM library, and the PAM library uses this value to look up a list of PAM modules to run.

AUTHTYPE and AUTHDATA are also both character strings. AUTHTYPE specifies the authentication type or format. The contents of AUTHDATA depend on AUTHTYPE.

Currently, two AUTHTYPEs are defined: "login", which specifies the traditional userid/password authentication mechanism; and "cram-md5", that specifies the challenge/response authentication mechanism defined by RFC 2095.

When AUTHTYPE is set to "login", AUTHDATA must be set to the userid, followed by a newline character, and the password, also followed by the newline character.

When AUTHTYPE is set to "cram-md5", AUTHDATA must be set to the base64-encoded challenge string followed by a newline character, then the base64-encoded response string, also followed by a newline character.

Before running the first authentication module, authmod sets up a pipe on file descriptor 3 to the first authentication module, and writes the following to the pipe: service<NL>AUTHTYPE<NL>AUTHDATA

<NL> represents the newline character. The pipe is immediately closed after writing this information. The authlib library does not use file descriptors 0, 1, and 2. They are preserved throughout the process.

The logic in an authentication module is usually like this:

int main(int argc, char **argv)
{
const char *service, *authtype;
char *authdata;

   authmod_init(argc, argv, &service, &authtype, &authdata);

   /* Attempt to accept this authentication request */

   if success
   {
       authmod_success(argc, argv);
   }

   if failed
   {
       authmod_fail(argc, argv)
   }

   if permanently failed
   {
       authmod_fail_completely();
   }
}

The authentication module calls authmod_init as the first order of business. authmod_init receives the argc and argv arguments as main received them. authmod_init retrieves the service, authentication type, and data, and returns them.

NOTE: There's a theoretical upper limit on the maximum number of characters read from the pipe on file descriptor 3. It is high enough that most people shouldn't worry about it (the total number of characters allowed cannot be more than 8189 characters on a typical Linux system).

The authentication module then proceeds to authenticate this request in some arbitrary fashion. The end result must be a call to one of three functions: authmod_success, authmod_fail, or authmod_fail_completely.

authmod_success and authmod_fail must receive the argc and argv arguments that were received by main. They remove argv[0] from the array, and use the remaining arguments to specify the next process to execute.

All three functions do not return, as they functions execute either the next process specified by command line arguments, or the login process again.

Before calling authmod_success, an authentication module must reset its user and group id to the ones appropriate for the authenticated account, change to the account's home directory, and perform any additional initialization logic deemed necessary.

If authmod_success is called, and the next process is another authentication module, the next module's authmod_init function will automatically call authmod_success itself, without returning to the main function.

authmod_fail should be called if the password has not been authenticated. If the next process is another authentication module, it will receive the service string, and authtype and authtype strings, via a pipe on file descriptor 3, and the next authentication module will get a crack at validating the password.

authmod_fail_completely should be called if the authentication module rejects the password, and the authentication module does not want to give any other authentication modules a chance to validate the userid and password. This is typically done if the authentication module believes that its the only authentication module that should be used to validate this authentication request, and that no other authentication modules should be able to do so. authmod_fail_completely reads the original arguments to the login process, then runs it.

Eventually, the authenticated client process will be executed. That's the last process specified on the original command line to the login process.

The only thing that the authentication client process needs to do is to make a call to the authmodclient() function as the very first order of business in main(). authmodclient takes no arguments. It checks whether or not any modules successfully authenticated the userid and password by calling authmod_success. In any other event, authmodclient() automatically executes the login process (without returning to main) to begin the authentication process again.

FUNCTIONS IN libauth.a

In addition to the authentication functions in libmodauth.a library, the libauth.a contains the following functions which may be useful when writing a new authentication module:

void authchangegroup(gid_t);
void authchangeuidgid(uid_t, gid_t);
void authchangeusername(const char *, const gid_t *);

An authentication module should call these function prior to calling authmod_success. These functions set the process's user and group IDs. Some systems feature supplementary group IDs in addition to standard group IDs, and you may not want the authenticated client process to inherit any auxiliary group IDs from the superuser login process. The configuration script for the libauth.a tries to detect if this is the case, and these functions will include code to remove all auxiliary group IDs.

authchangegroup changes the process's group ID to the one specified by its argument. authchangeuidgid changes both the group and the user ID. authchangeusername looks up the userid and the groupid in /etc/passwd. If the second argument to authchangeusername is not NULL, it points to a group ID which will override the group ID specified in the system's password database for this user. If it is NULL, the group ID and any auxiliary group IDs are retrieved from the system's password database (the auxiliary group IDs are reset in all other cases).

LOW LEVEL AUTHENTICATION PROTOCOL

The actual authentication protocol uses environment variables and pipes to communicate authentication information between modules. All of this functionality is implemented by high level functions in the libauthmod.a library. This documentation is provided in the event it is desired to put together a compatible authentication module without using the code from libauthmod.a.

The login process performs the following procedure prior to running the first authentication module:

1) The AUTHENTICATED environment variable is cleared (set to an empty string). argv[0] is checked to make sure it contains a full pathname, and its contents are used to set the AUTHUSER environment variable. If AUTHUSER is already set, argv[0] is ignored. If argv[0] does not contain a full pathname, AUTHUSER must already be set prior to running this process.

2) wait(2) is repeatedly called to reap any zombie child processes that are a byproduct of this authentication protocol. wait(2) is called repeatedly until there are no more child processes left.

3) The AUTHARGC environment variable is checked. If it is not defined it means that the process was invoked for the first time, by a system daemon process. If AUTHARGC is defined it means that this is not the first time this process was executed for this connection, and the previous authentication request failed.

4) If necessary, AUTHARGC is set to contain the decimal value of argc, as received by main.

5) The environment variables ARGV0, ARGV1, up to ARGVn, are initialized to the contents of the argv array, as received by main. ARGV0 is set to the contents of argv[0], ARGV1 is set to the contents of ARGV[2], and so on.

6) If the login process wants to have an expiration time for the login attempt, then it should set the AUTHEXPIRE environment variable the first time the login process is called. AUTHEXPIRE should be set to the calculated expiration time, based on the system clock. Subsequent invocations (after failed authentication attempts) compare the system clock against the contents of AUTHEXPIRE to check if the login has expired. If not, it's possible to calculate how much time is left, and set the alarm clock to kill the process. The alarm clock is cleared if the next authentication attempt is entered before the alarm clock goes off.

7) The login process then tries read a potential userid and password, in some sort of fashion, taking into account.

8) After obtaining the userid and password, the authentication user process kills the expiration timer (if it was set), and creates a pipe. It forks, and the parent process connects the output of the pipe to file descriptor 3, then executes the next process specified by the command line arguments. The child process writes the following text to the pipe:

service<NL>authtype<NL>authdata

service, authtype, and authdata, comprise the authentication request, as defined previously. They are separated by a newline character, <NL>. A pipe is used to send the authentication request to the authentication module instead of command line arguments or environment variables. That's because you do not want the password readable by anyone who happens to run ps(1) at the same time. After writing the authentication request to the pipe, the child process quickly terminates.

An authentication module performs the following steps:

1) Checks the AUTHENTICATED environment variable. If it is not empty, it means that some previous authentication module successfully authenticated the userid and the password, so the authentication module immediately executes the next process specified by the command line. Otherwise, the module reads the userid, password, and service from file descriptor 3 which is immediately closed afterwards.

2) The authentication module calls wait(2) repeatedly to reap any zombie child processes.

3) Following that, the authentication module attempt to authenticate this request. There are three possible outcomes.

4) If the request is successfully authenticated, the module sets the process's userid, groupid, then changes to the account's home directory. The following environment variables are set: AUTHENTICATED is set to the userid; AUTHFULLNAME is set to the user's "full name"; AUTHADDR is set to the user's return address. After these environment variables are set, the next process specified on the command line is executed.

5) If the authentication request is rejected, the module process creates a pipe, forks, the parent process connects the output side of the pipe to file descriptor 3, then executes the next process specified by command line arguments line arguments. The child process writes the authentication request to the pipe, then immediately terminates. This allows the next authentication module, if present, to have the opportunity to authenticate this request.

6) In order to permanently reject the authentication request, without invoking any additional authentication modules, the authentication module must execute the process specified by the AUTHUSER environment variable. The command line arguments for the process must be taken from the AUTHARGVn series environment variables, the number of which is given by the ARGC environment variable.

An authenticated client process of this authentication library must take the following steps as the first order of business when it starts:

1) Repeatedly call wait(2) to reap any zombie child processes.

2) Verify that the AUTHENTICATED environment variable is not empty. If not, it means that none of the authentication modules successfully authenticated this request, so the process specified by AUTHUSER must be immediately invoked, with command line arguments set by the contents of AUTHARGVn series of environment variables.

3) Otherwise, the client process is considered to be authenticated, and ready for business.

BUGS

File descriptor 3 is used in order to avoid messing with program's stdin, stdout, and stderr.

SEE ALSO

courier(8), userdb(8)