authuserdb.c (auth_cram): Get AFS tokens, so that vmail works.
[hcoop/debian/courier-authlib.git] / README_authlib.html.in
CommitLineData
d9898ee8 1<?xml version="1.0"?>
2<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><title>Courier Authentication Library</title><link rel="stylesheet" href="style.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.72.0"/><link rel="start" href="#authlib" title="Courier Authentication Library"/><link rel="next" href="#authpwd" title="The authpwd authentication module"/><link xmlns="" rel="stylesheet" type="text/css" href="manpage.css"/><meta xmlns="" name="MSSmartTagsPreventParsing" content="TRUE"/><link xmlns="" rel="icon" href="icon.gif" type="image/gif"/><!--
3
4Copyright 1998 - 2007 Double Precision, Inc. See COPYING for distribution
5information.
6
7--></head><body><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h1 class="title"><a id="authlib" shape="rect"> </a>Courier Authentication Library</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="#authpwd" shape="rect">The <code class="literal">authpwd</code> authentication module</a></span></dt><dt><span class="sect1"><a href="#authshadow" shape="rect">The <code class="literal">authshadow</code> authentication module</a></span></dt><dt><span class="sect1"><a href="#authpam" shape="rect">The <code class="literal">authpam</code> authentication module</a></span></dt><dt><span class="sect1"><a href="#authpipe" shape="rect">The <code class="literal">authpipe</code> authentication module</a></span></dt><dt><span class="sect1"><a href="#authpipeproto" shape="rect">The <code class="literal">authpipe</code> protocol</a></span></dt><dt><span class="sect1"><a href="#authuserdb" shape="rect">The <code class="literal">authuserdb</code> authentication module</a></span></dt><dd><dl><dt><span class="sect2"><a href="#userdbprimer" shape="rect">A brief <code class="literal">userdb</code> primer</a></span></dt><dt><span class="sect2"><a href="#userdbsimple" shape="rect">A simple userdb setup</a></span></dt><dt><span class="sect2"><a href="#userdbcomplex" shape="rect">Large virtual domain farm</a></span></dt><dt><span class="sect2"><a href="#moreuserdb" shape="rect">Beyond <code class="literal">userdb</code></a></span></dt></dl></dd><dt><span class="sect1"><a href="#authvchkpw" shape="rect">The <code class="literal">authvchkpw</code> authentication module</a></span></dt><dt><span class="sect1"><a href="#authmysql" shape="rect">The <code class="literal">authmysql</code> authentication module</a></span></dt><dt><span class="sect1"><a href="#authpgsql" shape="rect">The <code class="literal">authpgsql</code> authentication module</a></span></dt><dt><span class="sect1"><a href="#authldap" shape="rect">The <code class="literal">authldap</code> authentication module</a></span></dt><dt><span class="sect1"><a href="#authcustom" shape="rect"><code class="literal">authcustom</code></a></span></dt><dt><span class="sect1"><a href="#options" shape="rect">Account options</a></span></dt><dt><span class="sect1"><a href="#authtest" shape="rect">Running <span><strong class="command">authtest</strong></span></a></span></dt><dd><dl><dt><span class="sect2"><a href="#pwchange" shape="rect">Changing account passwords</a></span></dt></dl></dd><dt><span class="sect1"><a href="#internals" shape="rect">Authentication internals</a></span></dt><dt><span class="sect1"><a href="#files" shape="rect">FILES</a></span></dt><dt><span class="sect1"><a href="#seealso" shape="rect">SEE ALSO</a></span></dt></dl></div><p>
8This library is used for two purposes:</p><p>
91. Read the name of a mail account.
10Determine the local account's home directory, and system userid and
11groupid.</p><p>
122. Read an account name, and a password.
13If valid, determine the account's home directory, system userid, and
14groupid.</p><p>
15The term "authentication" is used in the following documentation to refer
16to either one of these two functions.
17The library contains several alternative authentication modules to choose
18from, described below.</p><p>
19The configuration file <code class="filename">@authdaemonrc@</code> contains several
20settings. The most important of them are:</p><div class="itemizedlist"><ul type="disc"><li><p>
21A list of authentication modules to activate.
22By default, this list includes all available authentication modules,
23even if some are not actually installed at the moment.
24When the authentication library is set up, only those authentication
25modules that can be supported by the operating system will be installed.
26Some of the listed modules may not actually be there,
27however that's not a problem.
28Any unavailable authentication modules will be ignored.
29Also, on some platforms certain authentication modules are installed by
30optional sub-packages.
31Installing the sub-package is the only action needed to make use of it.</p><p>
32The only time the list of authentication modules need to be adjusted is
33when an available authentication module must be disabled for some reason.
34This should only be needed in the most unusual circumstances.</p></li><li><p>
35Number of authentication processes.
36The default setting is to start five authentication processes, which should be
37sufficient for normal usage.
38Try increasing this setting if its taking too long to log into an account,
39and you have determined that this is not due to a bottleneck in the whatever
40authentication database you're using (LDAP, MySQL, or PostgreSQL).</p><p>
41An authentication request must be completed within thirty seconds, otherwise
42it gets rejected.
43When authentication requests come in faster than all five authentication
44processes can get to them, delays build up, and the timer is ticking.
45If all the activity maxes out the CPU or I/O bandwidth,
46nothing can be done about it, short
47of getting another server. However if there's plenty of available CPU and
48I/O, increasing the number of processes will do the trick.</p></li></ul></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="authpwd" shape="rect"> </a>The <code class="literal">authpwd</code> authentication module</h2></div></div></div><p>
49This modules obtains account information and passwords from the
50<code class="filename">/etc/passwd</code> file.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
51This module doesn't actually read the <code class="filename">/etc/passwd</code>
52file, it uses the C library's getpw() functions.
53The C library implementation could use any mechanism to obtain the equivalent
54information.</p></div></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="authshadow" shape="rect"> </a>The <code class="literal">authshadow</code> authentication module</h2></div></div></div><p>
55This module is a version of the <code class="literal">authpwd</code> module that
56reads passwords
57from <code class="filename">/etc/shadow</code> (the C library's getsp()
58functions).</p></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="authpam" shape="rect"> </a>The <code class="literal">authpam</code> authentication module</h2></div></div></div><p>
59This modules uses the system's PAM library
60(pluggable authentication modules) for authentication.
61This is, essentially, a way to use existing PAM modules for authentication.
62Note, however, that the authenticated account's home directory, userid and
63groupid are still read from the <code class="filename">/etc/passwd</code> file,
64since PAM functionality is limited to validating account passwords.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
65Not all PAM modules are compatible with Courier's authentication library.
66PAM modules that make use of PAM's session functions, or authentication token
67functions, like <code class="literal">pam_krb5</code> will not work with Courier.</p></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
68Additional configuration steps will be required to set up
69the PAM library to authenticate Courier's services.
70Courier's IMAP and POP3 servers, for example, require that the
71“<span class="quote">imap</span>” and “<span class="quote">pop3</span>” PAM service to be
72configured.</p><p>
73The specific configuration steps differ from system to system.
74Consult the system documentation for more information.
75It might be tempting to throw in a towel and use
76<code class="literal">authshadow</code> or <code class="literal">authpwd</code>
77if you cannot figure out how to install PAM support,
78however that is not advisable.
79It is highly recommended to use
80<code class="filename">authpam</code> wherever the PAM library is available.</p><p>
81The exact configuration procedure depends on the PAM implementation.
82Most PAM libraries use configuration files in the
83<code class="filename">/etc/pam.d</code> directory.
84Therefore, it will be necessary to install the configuration files
85<code class="filename">/etc/pam.d/imap</code> and
86<code class="filename">/etc/pam.d/pop3</code>. Similarly, Courier's webmail
87server, SqWebMail, uses <code class="filename">/etc/pam.d/webmail</code>, and
88its optional calendar component uses <code class="filename">/etc/pam.d/webmail</code>.
89Courier-MTA's authenticated SMTP component uses the
90<code class="filename">/etc/pam.d/smtp</code> service.</p><p>
91In nearly all cases all these configuration files will specify an
92identical PAM library configuration for all services.
93The exact configuration details are site-specific.
94Here's an example of a PAM configuration file for a recent version of the
95most common PAM library:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
96auth required pam_nologin.so
97auth required pam_stack.so service=system-auth
98account required pam_stack.so service=system-auth
99session required pam_stack.so service=system-auth
100</pre></div><p>
101Again, the actual configuration is site specific.
102Examine the contents of existing configuration files in
103<code class="filename">/etc/pam.d</code> for similar services (if there's
104<code class="filename">/etc/pam.d/ppp</code>
105it's often a good example to follow) in order
106to derive the correct setup for Courier.</p><p>
107Older PAM libraries use a single configuration file, usually
108<code class="filename">/etc/pam.conf</code>.
109Append Courier-specific PAM settings to this configuration file, again
110using settings for existing services as a guide.
111For example:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
112imap auth required pam_unix.so try_first_pass
113imap account required pam_unix.so
114imap session required pam_permit.so
115pop3 auth required pam_unix.so try_first_pass
116pop3 account required pam_unix.so
117</pre></div><p>
118Some PAM libraries use
119<code class="filename">pam_pwdb.so</code> instead of
120<code class="filename">pam_unix.so</code>; consult the PAM library's
121documentation for more information.</p></div></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="authpipe" shape="rect"> </a>The <code class="literal">authpipe</code> authentication module</h2></div></div></div><p>This is a generic plug-in module that runs an external script,
122or a program, in response to authentication requests.</p><p>The external program reads from stdin and writes to stdout. It
123can be persistent and handle many authentication requests. Only one request
124will be sent to it at a time; each authdaemon process starts its own copy of
125the external script.</p><p>The location of the external program is set by the
126<code class="literal">--with-pipeprog</code> configure option,
127which defaults to
128<code class="filename">@sysconfdir@/authlib/authProg</code>. A sample program
129is included in the courier-authlib source.</p></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="authpipeproto" shape="rect"> </a>The <code class="literal">authpipe</code> protocol</h2></div></div></div><p>
130authpipe uses the same protocol as authdaemon clients use to communicate
131with authdaemond.</p><p>There are four possible requests: <code class="literal">PRE</code>,
132<code class="literal">AUTH</code>, <code class="literal">PASSWD</code> and
133<code class="literal">ENUMERATE</code>. Apart from <code class="literal">AUTH</code>, each
134request is a single line terminated by newline.
135</p><div class="variablelist"><dl><dt><span class="term">PRE . <em class="replaceable"><code>authservice</code></em> <em class="replaceable"><code>username</code></em> <span class="emphasis"><em>&lt;newline&gt;</em></span></span></dt><dd><p>Look up data for an account.
136 <em class="replaceable"><code>authservice</code></em> identifies the service the
137 user is trying to use - e.g. pop3, imap, webmail etc.</p><p>If the account exists, return the account
138 data as a series of ATTR=value newline-terminated lines, followed by a
139 period on a line of its own. Valid attributes are:
140 </p><pre class="screen" xml:space="preserve">
141 USERNAME=username -- system account which owns mailbox (name)
142 UID=uid -- system account which owns mailbox (numeric uid)
143 GID=gid -- numeric groupid
144 HOME=homedir -- home directory
145 ADDRESS=addr -- e-mail address
146 NAME=name -- full name
147 MAILDIR=maildir -- Maildir relative to home directory
148 QUOTA=quota -- quota string: maxbytesS,maxfilesC
149 PASSWD=cryptpasswd -- encrypted password
150 PASSWD2=plainpasswd -- plain text password
151 OPTIONS=acctoptions -- option1=val1,option2=val2,...
152 .
153 </pre><p>
154 Of these, it is mandatory to return ADDRESS, HOME, GID, and either UID
155 or USERNAME; the others are optional.
156 </p><p>If the account is not known, return <code class="literal">FAIL</code><span class="emphasis"><em><code class="literal">&lt;newline&gt;</code></em></span>.
157 If there is a temporary failure, such as a database being down, authProg
158 should terminate (thereby closing stdin/stdout) without sending any
159 response. authdaemon will restart the pipe module for the next
160 request, thus ensuring it is properly reinitialized.
161 </p></dd><dt><span class="term">AUTH <em class="replaceable"><code>len</code></em><span class="emphasis"><em>&lt;newline&gt;</em></span><em class="replaceable"><code>len-bytes</code></em></span></dt><dd><p>
162 Validate a login attempt. The AUTH line is followed by
163 <span class="emphasis"><em>len-bytes</em></span> of authentication data, which does not
164 necessarily end with a newline. The currently defined authentication
165 requests are:
166 </p><pre class="screen" xml:space="preserve">
167 login \n username \n password [\n] -- plaintext login
168 cram-md5 \n challenge \n response [\n] -- base-64 encoded challenge and response
169 cram-sha1 \n challenge \n response [\n] -- ditto
170 cram-sha256 \n challenge \n response [\n] -- ditto
171 </pre><p>
172 In the case of success, return the complete set of
173 account parameters in the same format as PRE, ending with a period on
174 a line of its own. In the case of failure (e.g. username does not exist,
175 password wrong, unsupported authentication type), return
176 <code class="literal">FAIL</code><span class="emphasis"><em><code class="literal">&lt;newline&gt;</code></em></span>.
177 If there is a temporary failure, such as a database being down, authProg
178 should terminate without sending any response.
179 </p><p>
180 Note: if the user provides a plaintext password and authenticates
181 successfully, then you can return it as PASSWD2 (plain text password)
182 even if the database contains an encrypted password. This is useful
183 when using the POP3/IMAP proxy functions of courier-imap.
184 </p></dd><dt><span class="term">PASSWD <em class="replaceable"><code>service</code></em><span class="emphasis"><em>&lt;tab&gt;</em></span>
185 <em class="replaceable"><code>username</code></em><span class="emphasis"><em>&lt;tab&gt;</em></span>
186 <em class="replaceable"><code>oldpasswd</code></em><span class="emphasis"><em>&lt;tab&gt;</em></span>
187 <em class="replaceable"><code>newpasswd</code></em><span class="emphasis"><em>&lt;tab&gt;</em></span>
188 <span class="emphasis"><em>&lt;newline&gt;</em></span>
189 </span></dt><dd><p>Request a password change for the given account: validate that
190 the oldpassword is correct, and if so, change it to the newpassword.
191 </p><p>Reply: the string
192 for success, or <code class="literal">FAIL</code><span class="emphasis"><em><code class="literal">&lt;newline&gt;</code></em></span> for
193 a data error (e.g. no such account, old password wrong, new password not
194 acceptable). In the case of a temporary failure, such as a database
195 being down, authProg should terminate without sending any response.
196 </p></dd><dt><span class="term">ENUMERATE <span class="emphasis"><em>&lt;newline&gt;</em></span></span></dt><dd><p>
197 Return a list of all accounts, one per line in the following format,
198 ending with a period on a line of its own:
199 </p><pre class="screen" xml:space="preserve">
200 username \t uid \t gid \t homedir \t maildir \t options \n
201 .
202 </pre><p>
203 If your module does not support the ENUMERATE command then return just
204 a period on a line of its own (which will still allow enumeration data
205 from other modules to be returned). In the case of a temporary failure,
206 such as a database being down or an error occuring mid-way through
207 returning account data, authProg should terminate before sending
208 the terminating period.
209 </p></dd></dl></div></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="authuserdb" shape="rect"> </a>The <code class="literal">authuserdb</code> authentication module</h2></div></div></div><p>
210This module
211uses a GDBM or a DB-based
212<a href="userdb.html" target="_top" shape="rect"><span class="citerefentry"><span class="refentrytitle">userdb</span>(8)</span></a> database.
213This module also incorporates userdb-based challenge-response authentication
214implementation that was done by a separate <code class="literal">authcram</code> module
215in previous versions of the Courier authentication library.</p><p>
216<code class="filename">@sysconfdir@/authlib/userdb</code> is a plain file that
217can be edited with any text editor.
218The file contains a list of account names, and their pertinent information.
219<code class="filename">@sysconfdir@/authlib/userdb</code> may alternatively be a
220directory containing plain text files, which are effectively concatenated
221together to form the actual list of accounts.
222The <span><strong class="command">makeuserdb</strong></span> script compiles the account information
223into a GDBM or DB database file, which can be quickly looked up.</p><p>
224<code class="filename">@sysconfdir@/authlib/userdb</code> is loosely equivalent in
225function to <code class="filename">/etc/passwd</code> and
226<code class="filename">/etc/shadow</code>, and contain analous information: account
227name, its numeric userid and groupid, home directory, and passwords.
228<code class="filename">@sysconfdir@/authlib/userdb</code> also contains additional
229Courier-specific metadata, such as account quotas and other account-specific
230settings.
231<code class="filename">@sysconfdir@/authlib/userdb</code> files can also be
232maintained by custom-written Perl scripts, instead of being edited
233by hand.</p><p>
234<code class="filename">@sysconfdir@/authlib/userdb</code>
235allows creation of virtual mail accounts that do not have a corresponding
236login account -- virtual mail accounts that can share the same, reserved,
237system userid.
238<code class="filename">@sysconfdir@/authlib/userdb</code>
239can also be used to completely supersede
240<code class="filename">/etc/passwd</code>.
241With many accounts it can be quite a drain to have to continuously linearly
242scan <code class="filename">/etc/passwd</code> in order to look up an account.
243Instead, a fast database lookup can retrieve the same information from the
244database file.
245Review the included manual pages, starting with
246<a href="userdb.html" target="_top" shape="rect"><span class="citerefentry"><span class="refentrytitle">userdb</span>(8)</span></a>, for more information.</p><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="userdbprimer" shape="rect"> </a>A brief <code class="literal">userdb</code> primer</h3></div></div></div><p>
247<code class="literal">userdb</code> is a way to implement many virtual mailboxes - many
248mailboxes that do not have to have a separate system userid allocated for
249each one, and there is no system login associated with each mailbox.
250<code class="literal">userdb</code> uses a database for mapping virtual addresses to physical
251maildirs. It should be scalable to thousands of mailboxes. It can also be
252used to replace linear searches of <code class="filename">/etc/passwd</code> with a database
253lookup, see
254<a href="pw2userdb.html" target="_top" shape="rect"><span class="citerefentry"><span class="refentrytitle">pw2userdb</span>(8)</span></a>.</p><p>
255Note - you still MUST use some valid system userid and groupid that is
256shared by all virtual mailboxes. Instead of allocating a single userid and
257groupid per each mailbox, the same userid and groupid is used for all of
258them.</p><p>
259This is a rough overview of using userdb. For additional information, read
260<a href="userdb.html" target="_top" shape="rect"><span class="citerefentry"><span class="refentrytitle">userdb</span>(8)</span></a>
261and
262<a href="makeuserdb.html" target="_top" shape="rect"><span class="citerefentry"><span class="refentrytitle">makeuserdb</span>(8)</span></a>. All the scripts will
263be installed in <code class="filename">@sbindir@</code>, so look for them there.</p><p>
264The best way to describe how <code class="literal">userdb</code> works is to try to create
265one virtual mail account. As mentioned before, virtual mailboxes still need
266one system account to be used for uid/gid purposes. Let's call this system
267account "vmail".</p></div><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="userdbsimple" shape="rect"> </a>A simple userdb setup</h3></div></div></div><p>
268This approach should be used if you do not have many virtual mailboxes.
269It's very simple, but quickly becomes cumbersome if you administer many
270virtual mailboxes.</p><p>Create an empty <code class="filename">@userdb@</code> file:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
271# cp /dev/null @userdb@
272# chmod 700 @userdb@</pre></div><p>
273<code class="filename">@userdb@</code> must have 700 permissions,
274since it will contain passwords.</p><p>
275Now, run the script <span><strong class="command">pw2userdb</strong></span>, as root.
276This script converts the
277contents of <code class="filename">/etc/passwd</code>
278to the <code class="filename">@userdb@</code> format
279(including the contents of <code class="filename">/etc/shadow</code>,
280this is why permissions
281on <code class="filename">@userdb@</code> must be 700). This script is usually used
282where you
283want to convert a very large <code class="filename">/etc/passwd</code> to
284<code class="filename">@userdb@</code>. <code class="literal">userdb</code> applications can now
285use a fast
286<code class="literal">userdb</code> database instead of a linear scan
287of <code class="filename">/etc/passwd</code>
288in order to look up system accounts. However, you probably don't want to
289use this feature right now, so what you want to do is take the output
290of <span><strong class="command">pw2userdb</strong></span>, and find the entry for the vmail account
291that you
292created earlier. Look for a line that starts with 'vmail' followed by tab,
293followed by familiar fields from <code class="filename">/etc/passwd</code>. Save the
294output of
295<span><strong class="command">pw2userdb</strong></span> in a temporary file, edit it, and remove
296everything
297except the line containing vmail, and the very next line, which is a special
298entry that maps vmail's userid back to the vmail record.</p><p>
299Here's what you might find in the output of
300<span><strong class="command">pw2userdb</strong></span>:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
301vmail uid=1012|gid=1012|home=/home/vmail|systempw=*
3021012= vmail</pre></div><p>
303The actual numerical values and the home directory location may vary.
304Save
305these two lines as <code class="filename">@userdb@</code>, and set the permissions on
306<code class="filename">@userdb@</code> to 700:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
307$ chmod 700 <code class="filename">@userdb@</code>
308</pre></div><p>
309Now, with that out of the way, let's really create a virtual account. In
310this example we'll create a virtual mailbox for 'john@example.com'.</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
311# su vmail
312$ cd ~vmail
313$ mkdir john-example
314$ maildirmake john-example/Maildir
315$ exit
316#
317</pre></div><p>
318You may need to specify a full path to your <span><strong class="command">maildirmake</strong></span>
319program. The end result is that you created
320<code class="filename">$HOME/john-example</code> in vmail's account, which
321can be thought of as a “<span class="quote">virtual home directory</span>” for
322“<span class="quote">john@example.com</span>”, that contains the account's maildir
323mailbox.</p><p>
324Now, let's connect the dots here, and create an entry in
325<code class="filename">@userdb@</code> for <code class="filename">john@example.com</code>:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
326# userdb "john@example.com" set home=/home/vmail/john-example \
327 uid=UUU gid=GGG
328</pre></div><p>
329This command runs the script named <code class="filename">userdb</code> , which is
330installed, by default in <code class="filename">@sbindir@</code>. Replace UUU and
331GGG with the userid and groupid of the vmail account. If you now look in
332<code class="filename">@userdb@</code>, you will see that a new record for
333“<span class="quote">john@example.com</span>”
334has been appended to the end of the file.</p><p>
335One more detail: we need to set the IMAP password for this
336mailbox:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
337# userdbpw | userdb "john@example.com" set imappw</pre></div><p>
338On most modern Linux and BSD distributions, you can specify the -md5
339option to <span><strong class="command">userdbpw</strong></span>, in order to use MD5 password hashes,
340instead of crypt. The traditional password function allows passwords only
341up to 8 characters long; everything in excess is ignored.
342The newer MD5 passwords, now supported by most modern systems, allow
343longer passwords.</p><p>
344Use "<code class="literal">systempw</code>" instead of
345"<code class="literal">imappw</code>" if you would like to use the same password for the POP3
346server, and for all other services.
347The "<code class="literal">imappw</code>" field is only checked by the IMAP server.
348If not
349defined, "<code class="literal">systempw</code>" is used instead. The field
350<code class="literal">pop3pw</code>
351is checked only by Courier's POP3 server. If it is
352not defined the POP3 server will check <code class="literal">systempw</code> too.</p><p>Finally, compile the database:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
353# makeuserdb
354</pre></div><p>
355This command creates the actual database, <code class="filename">@userdb@.dat</code> and
356<code class="filename">@userdb@shadow.dat</code> from the plain text file
357<code class="filename">@userdb@</code>. Courier will now start accepting logins to this
358mailbox. Adding and removing mailboxes can be done while Courier is
359running.</p><p>
360Courier reads <code class="filename">@userdb@.dat</code> and
361<code class="filename">@userdb@shadow.dat</code> only. The plain text source,
362<code class="filename">@userdb@</code> is not read by Courier itself. Changes take
363effect
364only when <span><strong class="command">makeuserdb</strong></span> runs.</p></div><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="userdbcomplex" shape="rect"> </a>Large virtual domain farm</h3></div></div></div><p>
365The previous approach used a single flat file, <code class="filename">@userdb@</code>.
366This
367will work for up to a couple of hundred accounts.
368An slightly different approach can scale to thousands of
369domains and mailboxes.</p><p>
370Instead of creating a <code class="filename">@userdb@</code> file, create a
371subdirectory:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
372# mkdir @userdb@
373# chmod 700 @userdb@
374</pre></div><p>
375Now, create <code class="filename">@userdb@/default</code>, containing pw2userdb's
376output
377for the vmail account, as previously described.</p><p>
378This time, you probably want to create all mailboxes for the same domain
379in a separate subdirectory:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
380# su - vmail
381$ cd ~vmail
382$ mkdir -p domains/example-com
383$ mkdir domains/example-com/john
384$ maildirmake domains/example-com/john
385$ exit
386</pre></div><p>
387The idea is that all the maildirs for <code class="literal">@example.com</code> will
388now be found
389in <code class="filename">~vmail/domains/example-com</code>. All maildirs for
390<code class="literal">domain.org</code> will be in
391<code class="filename">~vmail/domains/domain.org</code>. The actual layout and naming
392conventions are entirely up to you to define.</p><p>Here's how configure <code class="filename">@userdb@</code>:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
393$ userdb "example-com/john@example.com" set \
394 home=/home/vmail/domains/example-com/john \
395 uid=UUU gid=GGG</pre></div><p>
396This creates the file <code class="filename">@userdb@/example-com</code> (the first
397parameter to the <span><strong class="command">userdb</strong></span> command), and appends a record named
398"john@example.com". You will store all <code class="literal">userdb</code> entries for
399<code class="literal">@example.com</code> in the file
400<code class="filename">@userdb@/example-com</code>. All
401entries for <code class="literal">@domain.org</code> will be maintained in
402<code class="filename">@userdb@/domain-org</code>, and so on.</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
403$ userdbpw | userdb "example-com/john@example.com" set imappw
404</pre></div><p>
405This sets the IMAP access password for this account. Finally:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
406$ makeuserdb
407</pre></div></div><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="moreuserdb" shape="rect"> </a>Beyond <code class="literal">userdb</code></h3></div></div></div><p>
408<code class="literal">userdb</code> is a simple, straightforward solution that scales
409to a couple of thousand of mail accounts, depending on the hardware.
410Beyond that, one of database-based modules will need to be used,
411such as
412<code class="literal">authldap</code>,
413<code class="literal">authmysql</code>,
414<code class="literal">authpgsql</code>.
415Since <code class="literal">userdb</code> is maintained as plain text files that
416are easily parsed by a script, migrating data from userdb will not be
417difficult.</p></div></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="authvchkpw" shape="rect"> </a>The <code class="literal">authvchkpw</code> authentication module</h2></div></div></div><p>
418This module uses
419the <code class="literal">vpopmail/vchkpw</code> library for authentication.</p></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="authmysql" shape="rect"> </a>The <code class="literal">authmysql</code> authentication module</h2></div></div></div><p>
420This module reads
421the list of mail accounts and passwords from a table in a
422MySQL database.
423The <code class="filename">@authmysqlrc@</code> configuration file defines the
424particular details regarding the MySQL database and the schema of the
425mail account table.</p></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="authpgsql" shape="rect"> </a>The <code class="literal">authpgsql</code> authentication module</h2></div></div></div><p>
426This module reads
427the list of mail accounts and passwords from a table in a
428PostgreSQL database.
429The <code class="filename">@authpgsqlrc@</code> configuration file defines the
430particular details regarding the PostgreSQL database and the schema of the
431mail account table.</p></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="authldap" shape="rect"> </a>The <code class="literal">authldap</code> authentication module</h2></div></div></div><p>
432This module reads
433the list of mail accounts and passwords from an LDAP directory.
434The <code class="filename">@authldaprc@</code> configuration file defines the
435particular details regarding the LDAP directory layout.</p><p>
436A suggested LDAP schema can be found in the file
437<code class="filename">authldap.schema</code>,
438which is included in Courier authentication library's source code, and
439may be installed on your system.</p></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="authcustom" shape="rect"> </a><code class="literal">authcustom</code></h2></div></div></div><p>
440This is a do-nothing module where custom authentication code
441can be added.
442This authentication module is just a stub that doesn't really do anything.
443It's purpose is to serve as a placeholder where custom authentication code
444can be easily implement.</p></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="options" shape="rect"> </a>Account options</h2></div></div></div><p>
445The authentication library has a facility for keep arbitrary
446“<span class="quote">name=value</span>”-type settings,
447called “<span class="quote">options</span>”, for individual accounts. This feature is
448only available with
449<code class="literal">userdb</code>,
450<code class="literal">LDAP</code>, <code class="literal">MySQL</code>, and
451<code class="literal">PostgresSQL</code>
452modules. Individual account options are not supported with
453system-based authentication modules (password/shadow files, or PAM).</p><p>
454See the
455<a href="auth_generic.html" target="_top" shape="rect"><span class="citerefentry"><span class="refentrytitle">auth_generic</span>(3)</span></a>
456for a description of option names used by various Courier packages.
457Other applications can make up names for their own settings, and
458use them in the same way.</p><p>
459Account options are specified via the authentication modules in the
460following manner:</p><div class="variablelist"><dl><dt><span class="term"><code class="literal">userdb</code></span></dt><dd><p>
461Use the <span><strong class="command">userdb</strong></span> command to set a field called
462"<code class="literal">options</code>". Example:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
463userdb user1@example.com set options=disableimap=1,sharedgroup=44
464makeuserdb
465</pre></div><p>
466The option text string here is
467"<code class="literal">disableimap=1,sharedgroup=44</code>".
468It specifies two options.</p></dd><dt><span class="term"><code class="literal">LDAP</code></span></dt><dd><p>
469Account options are defined by the <code class="literal">LDAP_AUXOPTIONS</code>
470setting in the <code class="filename">authldaprc</code> configuration file.
471<code class="literal">LDAP_AUXOPTIONS</code> consists of a comma-separated list of
472"<code class="literal">attribute=setting</code>". "attribute" is the name of an LDAP
473attribute, and "setting" is the corresponding account setting name. The
474value of the attribute becomes the value of the setting. Unless you
475value your sanity, the names of LDAP attributes should be the same as
476the actual setting names (in which case "=setting" may be dropped and
477<code class="literal">LDAP_AUXOPTIONS</code> becomes a simple comma-separated list of
478supported settings), but they don't have to be.</p><p>
479<code class="literal">LDAP_AUXOPTIONS</code> is nothing more than a simple mapping
480of LDAP attributes to account settings. A <code class="literal">LDAP_AUXOPTIONS</code>
481of "shared=sharedgroup,disableimap" means that the LDAP attribute
482called "shared" contains the "sharedgroup" setting, as described
483previously; and an LDAP attribute of disableimap contains the setting
484of the same name.</p></dd><dt><span class="term"><span class="application">MySQL</span>, and <span class="application">PostgreSQL</span></span></dt><dd><p>
485Account options are defined by <code class="literal">MYSQL_AUXOPTIONS_FIELD</code>
486or <code class="literal">POSTGRESQL_AUXOPTIONS_FIELD</code>, in its corresponding
487configuration file. In the most simplest case, add a character field to
488the database, and put the field name into the
489<code class="literal">MYSQL_AUXOPTIONS_FIELD</code> or
490<code class="literal">POSTGRESQL_AUXOPTIONS_FIELD</code> configuration file setting.
491For each account, the character field should contain the literal option
492string. Yes, you'll just put "shared=sharedgroup,disableimap"
493literally, in that field.</p><p>
494Fortunately, there is a cleaner way to do this, which avoid driving
495a database designer batty. Keep in mind that the contents of
496<code class="literal">MYSQL_AUXOPTIONS_FIELD</code>/<code class="literal">POSTGRESQL_AUXOPTIONS_FIELD</code>
497are simply inserted directly into the SQL query that fetches the
498account information. Both MySQL and PostgreSQL have a rich SQL that can
499be used to manufacture a suitable option string from plain,
500garden-variety, database fields. That is, you may define individual
501table fields like "disableimap", and "disablepop3", then provide a
502suitable (albeit ugly) SQL fragment that combines them together into
503the expected option string. An example of such an SQL string is
504provided in the comments portion of the configuration file.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
505When using the alternative custom query option, the option string
506 is the last field that the custom SQL query should return.</p></div></dd></dl></div></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="authtest" shape="rect"> </a>Running <span><strong class="command">authtest</strong></span></h2></div></div></div><p>
507The <span><strong class="command">authtest</strong></span> command may be used to verify that the
508authentication library is working:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
509authtest userid
510authtest userid password
511authtest userid password newpassword
512authenumerate</pre></div><p>
513Running
514<span><strong class="command">authtest</strong></span>
515with one argument should display the selected account's
516home directory, userid, groupid,
517and other related data.
518The second argument to
519<span><strong class="command">authtest</strong></span>,
520if supplied, specifies the account's password.
521The two argument form of
522<span><strong class="command">authtest</strong></span>
523validates the password, and displays an indication whether the given
524password is valid, or not.
525The three argument form of the
526<span><strong class="command">authtest</strong></span>
527command attemps to change the account's password.
528The second argument is the old password, the third argument is the
529new password.</p><p>
530See <a href="README.authdebug.html" target="_top" shape="rect"><code class="filename">README.authdebug.html</code></a> for more information.</p><div class="sect2" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="pwchange" shape="rect"> </a>Changing account passwords</h3></div></div></div><p>For the virtual domain modules (<code class="literal">authldap</code>,
531<code class="literal">authmysql</code>, <code class="literal">authpgsql</code> and friends) changing the
532login is a no-brainer. The tricky situation is when SqWebMail uses system
533passwords to log in (the <code class="literal">authpwd</code>, <code class="literal">authshadow</code>, or
534<code class="literal">authpam</code> authentication module). Different systems use different
535ways to keep login passwords. Many systems use the traditional
536<code class="filename">/etc/passwd</code> and <code class="filename">/etc/shadow</code> files. Other systems
537use a binary database; other systems use NIS. And on some systems the
538password file lookup library is a wrapper that goes against an external LDAP
539directory, or a database. For maximum compatibility, SqWebMail changes login
540passwords by running the <span><strong class="command">passwd</strong></span> command. This is the traditinal
541*nix command that changes login passwords. <span><strong class="command">passwd</strong></span> is an
542interactive command. It's normally run from a terminal.
543 SqWebMail uses an
544<span><strong class="command">expect</strong></span> script - as mentioned in
545the introduction - to answer interactive
546prompts from <span><strong class="command">passwd</strong></span>. The <span><strong class="command">expect</strong></span> script expects to
547get a plain, garden-variety, <span><strong class="command">passwd</strong></span> command, which acts
548something like this:</p><div class="informalexample"><pre class="programlisting" xml:space="preserve">
549 # passwd
550 Changing password for luser
551 (current) UNIX password: (old password typed here)
552 New UNIX password: (new password typed here)
553 Retype new UNIX password: (new password retyped here)
554 passwd: all authentication tokens updated successfully
555 #
556</pre></div><p>
557Systems that use a <span><strong class="command">passwd</strong></span> command with very different prompts
558may find that the default <span><strong class="command">expect</strong></span> script will fail. In which case
559it will be necessary to tweak the <span><strong class="command">expect</strong></span> script to match the
560prompts from the system's <span><strong class="command">passwd</strong></span> command.</p><p>
561Modern systems use a <span><strong class="command">passwd</strong></span> command that rejects "bad"
562passwords - passwords that are based on dictionary words, are too short, or
563are obvious for other reasons. When testing the ability to change
564system passwords be sure to use randomly-generated gibberish for the test
565passwords. Otherwise, the default <span><strong class="command">expect</strong></span> script will
566actually be
567working, but you won't be the wiser. For security reasons, the actual
568messages from <span><strong class="command">passwd</strong></span> will not be shown by.</p><p>
569The <span><strong class="command">expect</strong></span> script is installed as
570<code class="filename">/usr/local/libexec/courier-authlib/authsystem.passwd</code>
571(assuming default options to the <span><strong class="command">configure</strong></span> script).</p></div></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="internals" shape="rect"> </a>Authentication internals</h2></div></div></div><p>
572The following structure describes an authentication module:</p><div class="blockquote"><blockquote class="blockquote"><div class="example"><a id="authstaticinfo" shape="rect"> </a><p class="title"><b>Example 1. struct authstaticinfo</b></p><div class="example-contents"><pre class="programlisting" xml:space="preserve">
573struct authstaticinfo {
574 const char *auth_name;
575 int (*auth_func)(const char *, const char *, char *, int,
576 int (*)(struct authinfo *, void *),
577 void *);
578 int (*auth_prefunc)(const char *, const char *,
579 int (*)(struct authinfo *, void *),
580 void *);
581 void (*auth_cleanupfunc)();
582 int (*auth_changepwd)(const char *, /* service */
583 const char *, /* userid */
584 const char *, /* oldpassword */
585 const char *); /* new password */
586
587 void (*auth_idle)();
588 /* Not null - gets called every 5 mins when we're idle */
589
590 void (*auth_enumerate)( void(*cb_func)(const char *name,
591 uid_t uid,
592 gid_t gid,
593 const char *homedir,
594 const char *maildir,
595 void *void_arg),
596 void *void_arg);
597 } ;
598</pre></div></div><br class="example-break" clear="none"/></blockquote></div><p>
599An authentication module is a shared library that defines a single function
600called
601“<span class="quote">courier_auth_<em class="replaceable"><code>NAME</code></em>_init</span>”, where
602“<span class="quote">NAME</span>” is the name of the authentication module.
603The shared library does not need to export any other symbols, this is the
604only function that needs to be exported.
605The function returns a pointer to the <span class="structname">authstaticinfo</span>
606structure.
607For example, the relevant code from the <code class="literal">authmysql</code> module is:
608</p><div class="blockquote"><blockquote class="blockquote"><div class="example"><a id="authmysqlex" shape="rect"> </a><p class="title"><b>Example 2. authmysql</b></p><div class="example-contents"><pre class="programlisting" xml:space="preserve">
609static struct authstaticinfo authmysql_info={
610 "authmysql",
611 auth_mysql,
612 auth_mysql_pre,
613 auth_mysql_cleanup,
614 auth_mysql_changepw,
615 auth_mysql_cleanup,
616 auth_mysql_enumerate};
617
618
619struct authstaticinfo *courier_authmysql_init()
620{
621 return &amp;authmysql_info;
622}
623</pre></div></div><br class="example-break" clear="none"/></blockquote></div><p>
624<code class="function">auth_func</code> points to a function that handles an
625authentication request. The function is invoked as follows:</p><div class="blockquote"><blockquote class="blockquote"><div class="example"><a id="auth_func" shape="rect"> </a><p class="title"><b>Example 3. auth_func</b></p><div class="example-contents"><pre class="programlisting" xml:space="preserve">
626int result=auth_func(const char *service, const char *authtype,
627 const char *authdata,
628 int (*callback_func)(struct authinfo *, void *),
629 void *callback_arg);
630</pre></div></div><br class="example-break" clear="none"/></blockquote></div><p>
631“<span class="quote">service</span>” is the name of the service being authenticated,
632such as “<span class="quote"><code class="literal">imap</code></span>” or
633“<span class="quote"><code class="literal">pop3</code></span>”.
634“<span class="quote">authtype</span>” defines the authentication format,
635and “<span class="quote">authdata</span>” is the actual authentication request.</p><p>
636Two authentication formats are defined at this time.
637The “<span class="quote">authtype</span>” string is set to one of the following
638strings:</p><div class="variablelist"><dl><dt><span class="term">“<span class="quote">login</span>”</span></dt><dd><p>
639Tradition userid/password authentication.
640<code class="literal">authdata</code> points to a string that consists of:
641the userid; a newline character; the password; a final newline
642character.</p></dd><dt><span class="term">“<span class="quote">cram-md5</span>”, or “<span class="quote">cram-sha1</span>”</span></dt><dd><p>
643Challenge/response authentication.
644<code class="literal">authdata</code> points to a string that consists of:
645the base64-encoded challenge; a newline character;
646the base64-encoded response string; and a final newline
647character. Furthermore, the base64-encoded response string consists of:
648the login ID, a space character, and the response as a hexadecimal
649string (yes, base64-encoding of the response string is not strictly
650necessary).</p></dd></dl></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
651Not all authentication modules may implement all authentication formats.
652An authentication module that does not implement a particular authentication
653format should handle it the same way as an invalid login ID.</p></div><p>
654The authentication function should return a negative value if the login ID
655is invalid. The authentication library will try the next authentication
656module.</p><p>
657The authentication function should return a positive value if the login ID
658is valid, but the password is invalid. The authentication library will not
659try any more authentication modules.</p><p>
660Otherwise, the authentication module should call the
661<code class="function">callback_func</code> function, and return the same value that's
662returned by this function.</p><p>
663The authentication module should pass through <code class="literal">callback_arg</code>
664to the callback function as a second argument.
665The first argument is a pointer to the
666<span class="structname">authinfo</span> structure, which is described in detail
667in the
668<a href="auth_generic.html" target="_top" shape="rect"><span class="citerefentry"><span class="refentrytitle">auth_generic</span>(3)</span></a>
669manual page.
670The authentication module is responsible for allocating this structure.
671After the callback function returns this structure can be deallocated.
672The authentication module initializes the following fields:</p><p>
673<code class="function">auth_pre_func</code> points to a function that obtains
674account information. The function is invoked as follows:</p><div class="blockquote"><blockquote class="blockquote"><div class="example"><a id="auth_pre_func" shape="rect"> </a><p class="title"><b>Example 4. auth_pre_func</b></p><div class="example-contents"><pre class="programlisting" xml:space="preserve">
675int auth_pre_func(const char *user, const char *service,
676 int (*callback)(struct authinfo *, void *), void *arg);
677</pre></div></div><br class="example-break" clear="none"/></blockquote></div><p>
678This function does the same thing as “<span class="quote">auth_func</span>” except that
679the password is not actually verified.
680If the account exists, the callback function is invoked with the
681same callback arguments.</p><p>
682<code class="function">auth_cleanup_func</code> points to a function that will be
683invoked just before the authentication module is uninstalled, giving it
684the opportunity for some last-minute cleanup.</p><p>
685<code class="function">auth_idle</code> points to a function that will be
686invoked when no authentication requests are received for a couple of minutes,
687giving the authentication module an opportunity to close any database
688connections, so that they do not get shut down by the server, for inactivity,
689resulting in an error the next time an authentication request is
690received.</p><p>
691<code class="function">auth_changepwd</code> points to a function that will be
692invoked to change a password on an account, as follows.</p><div class="blockquote"><blockquote class="blockquote"><div class="example"><a id="auth_changepwd" shape="rect"> </a><p class="title"><b>Example 5. auth_changepwd</b></p><div class="example-contents"><pre class="programlisting" xml:space="preserve">
693int auth_changepwd(const char *service, const char *user,
694 const char *oldpw, const char *newpw);
695</pre></div></div><br class="example-break" clear="none"/></blockquote></div><p>
696<code class="literal">service</code> is the name of the service whose password is to
697be changed (such as “<span class="quote">imap</span>” or “<span class="quote">pop3</span>”).
698<code class="function">auth_changepwd</code> should return 0 if the password was
699changed succesfully, a negative value if <code class="literal">user</code> is invalid
700(the next authentication module will be tried), or a positive value if
701the password change request failed (no more modules will be tried).</p><p>
702<code class="function">auth_enumerate</code> points to a function that enumerates
703the list of all login IDs known to the authentication module.
704The first argument <code class="function">auth_enumerate</code> is a callback
705function. <code class="function">auth_enumerate</code> invokes the callback
706function once for each login ID, supplying the login ID, the userid,
707groupid, home directory and maildir as arguments.
708The last argument to the callback function is passed through from the
709second argument to <code class="function">auth_enumerate</code>.</p><p>
710After enumerating all login IDs <code class="function">auth_enumerate</code> calls
711the callback function one last time, with a NULL pointer for the login ID,
712then returns. If an error is encountered while enumerating the login IDs,
713<code class="function">auth_enumerate</code> terminates without invoking
714the callback function with a NULL login ID.</p></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="files" shape="rect"> </a>FILES</h2></div></div></div><p>
715<code class="filename"> @authdaemonrc@</code> - <span><strong class="command">authdaemond</strong></span> configuration file</p><p>
716<code class="filename"> @authldaprc@</code> - <span><strong class="command">authldap</strong></span> configuration file</p><p>
717<code class="filename"> @authmysqlrc@</code> - <span><strong class="command">authmysql</strong></span> configuration file</p><p>
718<code class="filename"> @authpgsqlrc@</code> - <span><strong class="command">authpgsql</strong></span> configuration file</p></div><div class="sect1" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="seealso" shape="rect"> </a>SEE ALSO</h2></div></div></div><p>
719<a href="courier.html" target="_top" shape="rect"><span class="citerefentry"><span class="refentrytitle">courier</span>(8)</span></a>,
720
721<a href="userdb.html" target="_top" shape="rect"><span class="citerefentry"><span class="refentrytitle">userdb</span>(8)</span></a></p></div></div></body></html>