old-www/HOWTO/html_single/User-Authentication-HOWTO/index.html

1939 lines
43 KiB
HTML

<HTML
><HEAD
><TITLE
> User Authentication HOWTO
</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><META
NAME="KEYWORD"
CONTENT="User Authentication"><META
NAME="KEYWORD"
CONTENT="user"><META
NAME="KEYWORD"
CONTENT="password"><META
NAME="KEYWORD"
CONTENT="PAM"><META
NAME="KEYWORD"
CONTENT="NIS"><META
NAME="KEYWORD"
CONTENT="/etc/passwd"><META
NAME="KEYWORD"
CONTENT="/etc/shadow"><META
NAME="KEYWORD"
CONTENT="/etc/group"><META
NAME="KEYWORD"
CONTENT="/etc/gshadow"></HEAD
><BODY
CLASS="ARTICLE"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="ARTICLE"
><DIV
CLASS="TITLEPAGE"
><H1
CLASS="TITLE"
><A
NAME="AEN2"
></A
>User Authentication HOWTO</H1
><H3
CLASS="AUTHOR"
><A
NAME="AEN4"
>Peter Hernberg</A
></H3
><P
CLASS="OTHERCREDIT"
><B
>Floris Lambrechts - </B
><SPAN
CLASS="CONTRIB"
> Language changes, various small fixes (v0.8).
</SPAN
></P
><P
CLASS="PUBDATE"
> 2000-05-02
<BR></P
><DIV
CLASS="REVHISTORY"
><TABLE
WIDTH="100%"
BORDER="0"
><TR
><TH
ALIGN="LEFT"
VALIGN="TOP"
COLSPAN="3"
><B
>Revision History</B
></TH
></TR
><TR
><TD
ALIGN="LEFT"
>Revision 0.8</TD
><TD
ALIGN="LEFT"
>2003-02-20</TD
><TD
ALIGN="LEFT"
>Revised by: fl</TD
></TR
><TR
><TD
ALIGN="LEFT"
COLSPAN="3"
>language changes, various small fixes</TD
></TR
><TR
><TD
ALIGN="LEFT"
>Revision 0.5</TD
><TD
ALIGN="LEFT"
>2000-05-15</TD
><TD
ALIGN="LEFT"
>Revised by: ph</TD
></TR
><TR
><TD
ALIGN="LEFT"
COLSPAN="3"
>added section on securing pam, added resources section</TD
></TR
><TR
><TD
ALIGN="LEFT"
>Revision 0.1</TD
><TD
ALIGN="LEFT"
>2000-05-02</TD
><TD
ALIGN="LEFT"
>Revised by: ph</TD
></TR
><TR
><TD
ALIGN="LEFT"
COLSPAN="3"
>initial version</TD
></TR
></TABLE
></DIV
><DIV
><DIV
CLASS="ABSTRACT"
><A
NAME="AEN7"
></A
><P
></P
><P
> Explains how user and group information is stored and how users are authenticated on a Linux system (PAM), and how to secure you system's user authentication.
</P
><P
></P
></DIV
></DIV
><HR></DIV
><DIV
CLASS="TOC"
><DL
><DT
><B
>Table of Contents</B
></DT
><DT
>1. <A
HREF="#AEN40"
>Introduction</A
></DT
><DD
><DL
><DT
>1.1. <A
HREF="#AEN42"
>How this document came to be</A
></DT
><DT
>1.2. <A
HREF="#AEN45"
>New versions</A
></DT
><DT
>1.3. <A
HREF="#AEN48"
>Feedback</A
></DT
><DT
>1.4. <A
HREF="#AEN51"
>Copyrights and Trademarks</A
></DT
><DT
>1.5. <A
HREF="#AEN64"
>Acknowledgements and Thanks</A
></DT
><DT
>1.6. <A
HREF="#AEN68"
>Assumptions about the reader</A
></DT
></DL
></DD
><DT
>2. <A
HREF="#AEN71"
>How User Information is Stored on Your System</A
></DT
><DD
><DL
><DT
>2.1. <A
HREF="#AEN73"
><TT
CLASS="FILENAME"
>/etc/passwd</TT
></A
></DT
><DT
>2.2. <A
HREF="#AEN81"
>Shadow passwords</A
></DT
><DT
>2.3. <A
HREF="#AEN95"
><TT
CLASS="FILENAME"
>/etc/group</TT
> and <TT
CLASS="FILENAME"
>/etc/gshadow</TT
></A
></DT
><DT
>2.4. <A
HREF="#AEN106"
>MD5 encrypted passwords</A
></DT
><DT
>2.5. <A
HREF="#AEN109"
>Sifting through the mess</A
></DT
></DL
></DD
><DT
>3. <A
HREF="#AEN115"
>PAM (Pluggable Authentication Modules)</A
></DT
><DD
><DL
><DT
>3.1. <A
HREF="#AEN118"
>Why</A
></DT
><DT
>3.2. <A
HREF="#AEN127"
>What</A
></DT
><DT
>3.3. <A
HREF="#AEN153"
>How</A
></DT
><DT
>3.4. <A
HREF="#AEN259"
>Getting more information</A
></DT
></DL
></DD
><DT
>4. <A
HREF="#AEN263"
>Securing User Authentication</A
></DT
><DD
><DL
><DT
>4.1. <A
HREF="#AEN266"
>A strong <TT
CLASS="FILENAME"
>/etc/pam.d/other</TT
></A
></DT
><DT
>4.2. <A
HREF="#AEN295"
>Disabling logins for user with null passwords</A
></DT
><DT
>4.3. <A
HREF="#AEN302"
>Disable unused services</A
></DT
><DT
>4.4. <A
HREF="#AEN307"
>Password-cracking tools</A
></DT
><DT
>4.5. <A
HREF="#AEN312"
>Shadow and MD5 passwords</A
></DT
></DL
></DD
><DT
>5. <A
HREF="#AEN316"
>Tying it all together</A
></DT
><DD
><DL
><DT
>5.1. <A
HREF="#AEN319"
>Apache + mod_auth_pam</A
></DT
><DT
>5.2. <A
HREF="#AEN322"
>Our example</A
></DT
><DT
>5.3. <A
HREF="#AEN326"
>Installing mod_auth_pam</A
></DT
><DT
>5.4. <A
HREF="#AEN343"
>Configuring PAM</A
></DT
><DT
>5.5. <A
HREF="#AEN360"
>Configuring Apache</A
></DT
><DT
>5.6. <A
HREF="#AEN373"
>Testing our setup</A
></DT
></DL
></DD
><DT
>6. <A
HREF="#AEN376"
>Resources</A
></DT
><DD
><DL
><DT
>6.1. <A
HREF="#AEN380"
>PAM</A
></DT
><DT
>6.2. <A
HREF="#AEN392"
>General Security</A
></DT
><DT
>6.3. <A
HREF="#AEN407"
>Offline Documentation</A
></DT
></DL
></DD
><DT
>7. <A
HREF="#AEN437"
>Conclusion</A
></DT
></DL
></DIV
><DIV
CLASS="SECT1"
><H1
CLASS="SECT1"
><A
NAME="AEN40"
></A
>1. Introduction</H1
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="AEN42"
></A
>1.1. How this document came to be</H2
><P
> When trying to add a number of (mostly unnecessary :) network services to my existing home network, I kept running into the problem of authentication, so I decided to figure out how authentication works on linux systems, write a HOWTO, and call it my senior project. I hope this document helps you understand this often-forgotten, but very important, aspect of system administration.
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN45"
></A
>1.2. New versions</H2
><P
> Unitl I get my domain up and running properly, the newest version of this document will be available from http://www.linuxdoc.org/.
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN48"
></A
>1.3. Feedback</H2
><P
> Comments, corrections, suggestions, flames, and flying saucer sightings can be sent to petehern@yahoo.com.
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN51"
></A
>1.4. Copyrights and Trademarks</H2
><P
> (c) 2000 Peter Hernberg
</P
><P
> This manual may be reproduced in whole or in part, without fee, subject to the following restrictions:
</P
><P
></P
><UL
><LI
><P
> The copyright notice above and this permission notice must be preserved complete on all complete or partial copies
</P
></LI
><LI
><P
> Any translation or derived work must be approved by the author in writing before distribution.
</P
></LI
><LI
><P
> If you distribute this work in part, instructions for obtaining the complete version of this manual must be included, and a means for obtaining a complete version provided.
</P
></LI
><LI
><P
> Small portions may be reproduced as illustrations for reviews or quotes in other works without this permission notice if proper citation is given. Exceptions to these rules may be granted for academic purposes: Write to the author and ask. These restrictions are here to protect us as authors, not to restrict you as learners and educators. Any source code (aside from the SGML this document was written in) in this document is placed under the GNU General Public License, available via anonymous FTP from the GNU archive.
</P
></LI
></UL
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN64"
></A
>1.5. Acknowledgements and Thanks</H2
><P
> Thanks to my family for putting up with me for 18 years. Thanks to the Debian folks for making such a sweet distro for me to play with. Thanks to <A
HREF="http://www.cgr.org/"
TARGET="_top"
>CGR</A
> for paying me to be a geek. Thanks to Sandy Harris for his helpful suggestions. Finally, I'd like thank the makers of ramen noodles, because I don't know how I'd live without them.
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN68"
></A
>1.6. Assumptions about the reader</H2
><P
> For the purpose of this document, it is assumed that the reader is comfortably with executing commands at the command line and editing text configuration files.
</P
></DIV
></DIV
><DIV
CLASS="SECT1"
><HR><H1
CLASS="SECT1"
><A
NAME="AEN71"
></A
>2. How User Information is Stored on Your System</H1
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="AEN73"
></A
>2.1. <TT
CLASS="FILENAME"
>/etc/passwd</TT
></H2
><P
> On almost all linux distributions (and commercial *nixes as well), user information is stored in <TT
CLASS="FILENAME"
>/etc/passwd</TT
>, a text file which contains the user's login, their encrypted password, a unique numerical user id (called the uid), a numerical group id (called the gid), an optional comment field (usually containing such items as their real name, phone number, etc.), their home directory, and their preferred shell. A typical entry in <TT
CLASS="FILENAME"
>/etc/passwd</TT
> looks something like this:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> pete:K3xcO1Qnx8LFN:1000:1000:Peter Hernberg,,,1-800-FOOBAR:/home/pete:/bin/bash
</PRE
></FONT
></TD
></TR
></TABLE
><P
> As you can see, it's pretty straight-forward. Each entry contains the six fields I described above, with each field separated by a colon. If this were as complex as user authentication got, there would be no need for this HOWTO.
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN81"
></A
>2.2. Shadow passwords</H2
><P
> Looking at your <TT
CLASS="FILENAME"
>/etc/passwd</TT
>, it's likely that you actually saw something like this:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> pete:x:1000:1000:Peter Hernberg,,,1-800-FOOBAR:/home/pete:/bin/bash
</PRE
></FONT
></TD
></TR
></TABLE
><P
> Where did the encrypted password go? Before I tell you where it went, a bit explanation is required.
</P
><P
> The <TT
CLASS="FILENAME"
>/etc/passwd</TT
> file, which contains information about all users, including their encrypted password, is readable by all users, making it possible for any user to get the encrypted password of everyone on the system. Though the passwords are encrypted, password-cracking programs are widely available. To combat this growing security threat, shadow passwords were developed.
</P
><P
> When a system has shadow passwords enabled, the password field in <TT
CLASS="FILENAME"
>/etc/passwd</TT
> is replaced by an "x" and the user's real encrypted password is stored in <TT
CLASS="FILENAME"
>/etc/shadow</TT
>. Because <TT
CLASS="FILENAME"
>/etc/shadow</TT
> is only readable by the root user, malicious users cannot crack their fellow users' passwords. Each entry in <TT
CLASS="FILENAME"
>/etc/shadow</TT
> contains the user's login, their encrypted password, and a number of fields relating to password expiration. A typical entry looks like this:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> pete:/3GJllg1o4152:11009:0:99999:7:::
</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN95"
></A
>2.3. <TT
CLASS="FILENAME"
>/etc/group</TT
> and <TT
CLASS="FILENAME"
>/etc/gshadow</TT
></H2
><P
> Group information is stored in <TT
CLASS="FILENAME"
>/etc/group</TT
>. The format is similar to that of <TT
CLASS="FILENAME"
>/etc/passwd</TT
>, with the entries containing fields for the group name, password, numerical id (gid), and a comma-separated list of group members. An entry in <TT
CLASS="FILENAME"
>/etc/group</TT
> looks like this:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> pasta:x:103:spagetti,fettucini,linguine,vermicelli
</PRE
></FONT
></TD
></TR
></TABLE
><P
> As you can see from the "x" in the password field, group passwords can be shadowed as well. Although groups almost never have their own passwords, it is worth noting that shadowed group password information is stored in <TT
CLASS="FILENAME"
>/etc/gshadow</TT
>.
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN106"
></A
>2.4. MD5 encrypted passwords</H2
><P
> Traditionally, unix passwords were encrypted with the standard crypt() function. (For more information on the crypt() function, see the crypt(3) manpage.) As computers grew faster, passwords encrypted with this function became easier to crack. As the internet emerged, tools for distributing the task of password-cracking across multiple hosts became available. Many 'newer' distributions ship with the option of encrypting passwords with the stronger MD5 hash algorithm. (For more information on the MD5 hash algorithm, consult RFC 1321.) While MD5 passwords will not eliminate the threat of password cracking, they will make cracking your passwords much more difficult.
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN109"
></A
>2.5. Sifting through the mess</H2
><P
> As you can see, there are a number of different ways user authentication information can be stored on your system (shadow passwords without MD5 encryption, <TT
CLASS="FILENAME"
>/etc/passwd</TT
> passwords with MD5 encryption, etc.). How do programs like <SPAN
CLASS="APPLICATION"
>login</SPAN
> and <SPAN
CLASS="APPLICATION"
>su</SPAN
> know how to verify your password? Worse yet, what if you wanted to change the way passwords are stored on your system? How will programs that need your password know that passwords are stored differently? PAM is the answer.
</P
></DIV
></DIV
><DIV
CLASS="SECT1"
><HR><H1
CLASS="SECT1"
><A
NAME="AEN115"
></A
>3. PAM (Pluggable Authentication Modules)</H1
><P
> Pluggable authentication modules are at the core of user authentication in any modern linux distribution.
</P
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN118"
></A
>3.1. Why</H2
><P
> Back in the good old days of linux, if a program, such as <SPAN
CLASS="APPLICATION"
>su</SPAN
>, <SPAN
CLASS="APPLICATION"
>passwd</SPAN
>, <SPAN
CLASS="APPLICATION"
>login</SPAN
>, or <SPAN
CLASS="APPLICATION"
>xlock</SPAN
>, needed to authenticate a user, it would simply read the necessary information from <TT
CLASS="FILENAME"
>/etc/passwd</TT
>. If it needed to change the users' password, it would simply edit <TT
CLASS="FILENAME"
>/etc/passwd</TT
>. This simple but clumsy method presented numerous problems for system administrators and application developers. As MD5 and shadow passwords became increasingly popular, each program requiring user authentication had to know how to get the proper information when dealing with a number of different schemes. If you wanted to change your user authentication scheme, all these programs had to be recompiled. PAM eliminates this mess by enabling programs to transparently authenticate users, regardless of how user information is stored.
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN127"
></A
>3.2. What</H2
><P
> Quoting from the <A
HREF="http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/pam.html"
TARGET="_top"
>Linux-PAM System Administrator's Guide</A
>: "It is the purpose of the Linux-PAM project to separate the development of privilege granting software from the development of secure and appropriate authentication schemes. This is accomplished by providing a library of functions that an application may use to request that a user be authenticated." With PAM, it doesn't matter whether your password is stored in /etc/passwd or on a server in Hong Kong. When a program needs to authenticate a user, PAM provides a library containing the functions for the proper authentication scheme. Because this library is loaded dynamically, changing authentication schemes can be done by simply editing a configuration file.
</P
><P
> Flexibility is one of PAM's greatest strengths. PAM can be configured to deny certain programs the right to authenticate users, to only allow certain users to be authenticated, to warn when certain programs attempt to authenticate, or even to deprive all users of login privileges. PAM's modular design gives you complete control over how users are authenticated.
</P
><DIV
CLASS="SECT3"
><HR><H3
CLASS="SECT3"
><A
NAME="AEN132"
></A
>3.2.1. Distributions that support pam.</H3
><P
> Nearly all popular distributions have supported PAM for some time. Here's an incomplete list of distributions that support PAM:
</P
><P
></P
><UL
><LI
><P
>Redhat since version 5.0</P
></LI
><LI
><P
>Mandrake since 5.2</P
></LI
><LI
><P
>Debian since version 2.1 (partial support in 2.1 -- complete support in 2.2)</P
></LI
><LI
><P
>Caldera since version 1.3</P
></LI
><LI
><P
>Turbolinux since version 3.6</P
></LI
><LI
><P
>SuSE since version 6.2</P
></LI
></UL
><P
> This list is certainly incomplete and possibly inaccurate. I'd appreciate it if you sent any corrections or additions to this list to <TT
CLASS="EMAIL"
>&#60;<A
HREF="mailto:petehern@yahoo.com"
>petehern@yahoo.com</A
>&#62;</TT
>.
</P
></DIV
><DIV
CLASS="SECT3"
><HR><H3
CLASS="SECT3"
><A
NAME="AEN150"
></A
>3.2.2. Installing PAM</H3
><P
> Installing PAM from scratch is long process, beyond the scope of this HOWTO. If PAM isn't installed on your system, you're probably running such an old version of your distribution that there are many other reasons to upgrade. If you really want to do it yourself, then you're certainly not the sort of person who needs any help from me. For all these reasons, I'm going to assume that you already have PAM installed.
</P
></DIV
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN153"
></A
>3.3. How</H2
><P
> Enough talk, let's dig in.
</P
><DIV
CLASS="SECT3"
><HR><H3
CLASS="SECT3"
><A
NAME="AEN156"
></A
>3.3.1. PAM configuration files</H3
><P
> PAM configuration files are stored in the <TT
CLASS="FILENAME"
>/etc/pam.d/</TT
> directory. (If you don't have <TT
CLASS="FILENAME"
>/etc/pam.d/</TT
> directory, don't worry, I'll cover that in the next section) Let's go over there and take a look.
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> <TT
CLASS="PROMPT"
>~$ </TT
><TT
CLASS="USERINPUT"
><B
>cd /etc/pam.d</B
></TT
>
<TT
CLASS="PROMPT"
>/etc/pam.d/$ </TT
><TT
CLASS="USERINPUT"
><B
>ls</B
></TT
>
chfn chsh login other passwd su xlock
<TT
CLASS="PROMPT"
>/etc/pam.d/$ </TT
>
</PRE
></FONT
></TD
></TR
></TABLE
><P
> Your system may have a few more or a few less files in this directory, depending on what's installed on your system. Whatever the details, you probably saw a file for each of the programs on your system that authenticate users. As you probably already guessed, each file contains the PAM authentication configuration for the program it's named after (except for the <TT
CLASS="FILENAME"
>other</TT
> file, which we'll talk about in a little bit). Let's take a look the PAM configuration file for login (I've condensed the file for the sake of simplicity):
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> <TT
CLASS="PROMPT"
>/etc/pam.d/$ </TT
><TT
CLASS="USERINPUT"
><B
>cat login</B
></TT
>
# PAM configuration for login
auth requisite pam_securetty.so
auth required pam_nologin.so
auth required pam_env.so
auth required pam_unix.so nullok
account required pam_unix.so
session required pam_unix.so
session optional pam_lastlog.so
password required pam_unix.so nullok obscure min=4 max=8
</PRE
></FONT
></TD
></TR
></TABLE
><P
> Before I dig into this file, I must mention a little something.
</P
></DIV
><DIV
CLASS="SECT3"
><HR><H3
CLASS="SECT3"
><A
NAME="AEN173"
></A
>3.3.2. A little something</H3
><P
> A small percentage of the readers are probably thinking, "Oh no! I don't have a /etc/pam.d directory! Your list of distributions says that my distribution includes PAM, but I can't find that directory. Without PAM, my life is empty and meaningless! What can I do?" Don't worry, all is not lost. If you know that your distribution includes PAM, but you have no <TT
CLASS="FILENAME"
>/etc/pam.d/</TT
> directory, then your PAM configuration is stored in <TT
CLASS="FILENAME"
>/etc/pam.conf</TT
>. Rather than being spread across several files, all your PAM configuration is stored in a single file. This adds a little twist to PAM configuration, but the proper adjustments are pointed out in section 3.3.4.
</P
></DIV
><DIV
CLASS="SECT3"
><HR><H3
CLASS="SECT3"
><A
NAME="AEN178"
></A
>3.3.3. Configuration syntax</H3
><P
> PAM configuration files have the following syntax:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> type control module-path module-arguments
</PRE
></FONT
></TD
></TR
></TABLE
><P
> Using the login configuration file (see above) as an example let's take a look a the syntax for PAM configuration files:
</P
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
>PAM configuration tokens</B
></P
><DL
><DT
><SPAN
CLASS="TOKEN"
>type</SPAN
></DT
><DD
><P
> The type token tells PAM what type of authentication is to be used for this module. Modules of the same type can be "stacked", requiring a user to meet multiple requirements to be authenticated. PAM recognizes four types:
</P
><P
></P
><DIV
CLASS="VARIABLELIST"
><DL
><DT
>account</DT
><DD
><P
> Determines whether the user is allowed to access the service, whether their passwords has expired, etc.
</P
></DD
><DT
>auth</DT
><DD
><P
> Determines whether the user is who they claim to be, usually by a password, but perhaps by a more sophistcated means, such as biometrics.
</P
></DD
><DT
>password</DT
><DD
><P
> Provides a mechanism for the user to change their authentication. Again, this usually their password.
</P
></DD
><DT
>session</DT
><DD
><P
> Things that should be done before and/or after the user is authenticed. This might included things such as mounting/unmounting the user home directory, logging their login/logout, and restricting/unrestricting the services available to the user.
</P
></DD
></DL
></DIV
><P
> In the login config file, we see at least one entry for each type. Since this the program that allows user to login (hence the name :), it's understandable that it needs to access all of the different types of authentication.
</P
></DD
><DT
><SPAN
CLASS="TOKEN"
>control</SPAN
></DT
><DD
><P
> The control token tells PAM what should be done in if authentication by this module fails. PAM recognizes four control types:
</P
><P
></P
><DIV
CLASS="VARIABLELIST"
><DL
><DT
>requisite</DT
><DD
><P
> Failure to authenticate via this module results in immediate denial of authentication.
</P
></DD
><DT
>required</DT
><DD
><P
> Failure also results in denial of authentication, although PAM will still call all the other modules listed for this service before denying authentication.
</P
></DD
><DT
>sufficient</DT
><DD
><P
> If authentication by this module is successful, PAM will grant authentication, even if a previous required module failed.
</P
></DD
><DT
>optional</DT
><DD
><P
> Whether this module succeeds or fails is only significant if it is the only module of its type for this service.
</P
></DD
></DL
></DIV
><P
> In the configuration file for login, we see nearly all of the different control types. Most of the required modules <TT
CLASS="FILENAME"
>are pam_unix.so</TT
> (the main authentication module), the single requisite module is <TT
CLASS="FILENAME"
>pam_securetty.so</TT
> (which makes sure the user is logging in on a secure console), and the only optional module is <TT
CLASS="FILENAME"
>pam_lastlog.so</TT
> (the module that retrieves information on the user's most recent login).
</P
></DD
><DT
><SPAN
CLASS="TOKEN"
>module-path</SPAN
></DT
><DD
><P
> The module-path tells PAM which module to use and (optionally) where to find it. Most configurations only contain the module's name, as is the case in our login configuration file. When this is the case, PAM looks for the modules in the default PAM module directory, normally <TT
CLASS="FILENAME"
>/usr/lib/security</TT
>. However, if your linux distribution conforms to the Filesystem Hierarchy Standard (FHS), PAM modules can be found in <TT
CLASS="FILENAME"
>/lib/security</TT
>.
</P
></DD
><DT
><SPAN
CLASS="TOKEN"
>module-arguments</SPAN
></DT
><DD
><P
> The module-arguments are arguments to be passed to the module. Each module has its own arguments. For example, in our login configuration, the "nulok" ("null ok", argument being passed to pam_unix.so module, indicating the a blank ("null") password is acceptable ("ok").
</P
></DD
></DL
></DIV
></DIV
><DIV
CLASS="SECT3"
><HR><H3
CLASS="SECT3"
><A
NAME="AEN246"
></A
>3.3.4. <TT
CLASS="FILENAME"
>pam.conf</TT
> configuration</H3
><P
> If your PAM configuration is stored in <TT
CLASS="FILENAME"
>/etc/pam.conf</TT
> rather than <TT
CLASS="FILENAME"
>/etc/pam.d/</TT
>, PAM configuration lines are a bit different. Rather than each service having its own configuration file, all configurations are stored in <TT
CLASS="FILENAME"
>/etc/pam.conf</TT
> with the service name as the first token in a configuration line. For example, the following line in <TT
CLASS="FILENAME"
>/etc/pam.d/login</TT
>:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> auth required pam_unix.so nulok
</PRE
></FONT
></TD
></TR
></TABLE
><P
> would become the following line in <TT
CLASS="FILENAME"
>/etc/pam.conf</TT
>:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> login auth required pam_unix.so nulok
</PRE
></FONT
></TD
></TR
></TABLE
><P
> Except for this minor difference, all the rest of the configuration PAM syntax applies.
</P
></DIV
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN259"
></A
>3.4. Getting more information</H2
><P
> For more information on configuring PAM and complete PAM module reference, consult the <A
HREF="http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/pam.html"
TARGET="_top"
>Linux-PAM System Administrator's Guide</A
>. This guide serves as a thorough and up-to-date reference on PAM configuration.
</P
></DIV
></DIV
><DIV
CLASS="SECT1"
><HR><H1
CLASS="SECT1"
><A
NAME="AEN263"
></A
>4. Securing User Authentication</H1
><P
> Many linux distributions ship with user authentication that is not adequately secure. This section discusses some of the ways you make user authentication secure on your system. While doing these things will make your system more secure, do not be so naive as to think they make you invulnerable.
</P
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN266"
></A
>4.1. A strong <TT
CLASS="FILENAME"
>/etc/pam.d/other</TT
></H2
><P
> All of the files in <TT
CLASS="FILENAME"
>/etc/pam.d/</TT
> contain the configuration for a particular service. The notable exception to this rule is the <TT
CLASS="FILENAME"
>/etc/pam.d/other</TT
> file. This file contains the configuration for any services which do not have their own configuration file. For example, if the (imaginary) <SPAN
CLASS="APPLICATION"
>xyz</SPAN
> service attempted authentication, PAM would look for a <TT
CLASS="FILENAME"
>/etc/pam.d/xyz</TT
> file. Not finding one, authentication for <SPAN
CLASS="APPLICATION"
>xyz</SPAN
> would be determined by the <TT
CLASS="FILENAME"
>/etc/pam.d/other</TT
> file. Since <TT
CLASS="FILENAME"
>/etc/pam.d/other</TT
> is the configuration to which PAM services fallback, it is important that it is secure. We will discuss two secure configurations of <TT
CLASS="FILENAME"
>/etc/pam.d/other</TT
>, one which is quite nearly paranoid and one which is gentler.
</P
><DIV
CLASS="SECT3"
><HR><H3
CLASS="SECT3"
><A
NAME="AEN278"
></A
>4.1.1. A paranoid configuration</H3
><P
> A paranoid configuration of <TT
CLASS="FILENAME"
>/etc/pam.d/other</TT
> is as follows:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> auth required pam_deny.so
auth required pam_warn.so
account required pam_deny.so
account required pam_warn.so
password required pam_deny.so
password required pam_warn.so
session required pam_deny.so
session required pam_warn.so
</PRE
></FONT
></TD
></TR
></TABLE
><P
> With this configuration, whenever an unknown service attempts to access any of the four configuration types, PAM denies authentication (via the pam_deny.so module) and then logs a syslog warning (via the pam_warn.so module). Short of a bug in PAM, this configuration is brutally secure. The only problem with that brutality is it may cause problems if your accidentally delete the configuration of another service. If your <TT
CLASS="FILENAME"
>/etc/pam.d/login</TT
> was mistakenly deleted, no one would be able to login!
</P
></DIV
><DIV
CLASS="SECT3"
><HR><H3
CLASS="SECT3"
><A
NAME="AEN285"
></A
>4.1.2. A kinder configuration</H3
><P
> Here's configuration that isn't quite so mean:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> auth required pam_unix.so
auth required pam_warn.so
account required pam_unix.so
account required pam_warn.so
password required pam_deny.so
password required pam_warn.so
session required pam_unix.so
session required pam_warn.so
</PRE
></FONT
></TD
></TR
></TABLE
><P
> This configuration will allow an unknown service to authenticate (via the pam_unix.so module), although it will not allow it to change the user's password. Although it allows authentication by unknown services, it logs a syslog warning whenever such a service attempts authentication.
</P
></DIV
><DIV
CLASS="SECT3"
><HR><H3
CLASS="SECT3"
><A
NAME="AEN290"
></A
>4.1.3. Choosing a <TT
CLASS="FILENAME"
>/etc/pam.d/other</TT
></H3
><P
> I would strongly reccomend that you implement the first <TT
CLASS="FILENAME"
>/etc/pam.d/other</TT
> configuration unless you have a very good reason not to. It always a good idea to be 'secure by default'. If you ever do need to grant a new service authentication privileges, you can simply create a PAM configuration file for that service.
</P
></DIV
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN295"
></A
>4.2. Disabling logins for user with null passwords</H2
><P
> On most linux systems, there a number of "dummy" user accounts, used to assign privileges to certain system services like ftp, webservers, and mail gateways. Having these accounts allows your system to be more secure, because if these services are compromised, an attacker will only gain the limited privileges available to the dummy account, rather than the full privileges of a service running as root. However, allowing these dummy account login privileges is a security risk, as they usually have blank (null) passwords. The configuration option that enables null passwords is the "nullok" module-argument. You'll want to remove this argument from any modules of 'auth' type for services that allow login. This is usually the login service, but it may also include services like rlogin and ssh. Hence, the following line in <TT
CLASS="FILENAME"
>/etc/pam.d/login</TT
>:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> auth required pam_unix.so nullok
</PRE
></FONT
></TD
></TR
></TABLE
><P
> should be changed to:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> auth required pam_unix.so
</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN302"
></A
>4.3. Disable unused services</H2
><P
> Looking at the files in <TT
CLASS="FILENAME"
>/etc/pam.d/</TT
>, you'll probably see configuration files for a number of programs you don't use and maybe even a few you've never heard of. Although allowing authentication to these services probably won't open any huge security holes, you're better off denying them authentication. The best way to disable PAM authentication for these programs is to rename these files. Not finding the file named after the service requesting authentication, PAM will fallback to the (hopefully) very secure <TT
CLASS="FILENAME"
>/etc/pam.d/other</TT
>. If you later find that you need one of these programs, you can simply rename the file to its original name and everything will work as it was intended.
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN307"
></A
>4.4. Password-cracking tools</H2
><P
> While password-cracking tools can be used by attackers to compromise a system, they can also be used by system administrators as proactive tool to ensure the strength of passwords on their system. The two most commonly used password-cracking tools are "crack" and "John the Ripper". Crack is probably included in your favorite distribution. John the Ripper can be obtained from <A
HREF="http://www.false.com/security/john/index.html"
TARGET="_top"
>http://www.false.com/security/john/index.html</A
>. Run the tools against your password database and you'll probably be surprised with what they come up with.
</P
><P
> Additionally, there is a PAM module which utilizes the crack library to check the strength of a users password whenever it changed. When this module is installed, the user can only change their password to one which meets the minimum password strength.
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN312"
></A
>4.5. Shadow and MD5 passwords</H2
><P
> As was discussed in the first section of this document, Shadow and MD5 passwords can make your system more secure. During the installation procedure, most modern distributions will ask whether you want to install MD5 and/or Shadow passwords. Unless you have a good reason not to, you should enable these. The process of converting from non-shadowed/non-MD5 passwords is a complicated process, and is beyond the scope of this document. The <A
HREF="http://www.linuxdoc.org/HOWTO/Shadow-Password-HOWTO.html"
TARGET="_top"
>Shadow Password HOWTO</A
> is outdated, but it might be of some help.
</P
></DIV
></DIV
><DIV
CLASS="SECT1"
><HR><H1
CLASS="SECT1"
><A
NAME="AEN316"
></A
>5. Tying it all together</H1
><P
> In this section, I'll give a simple example which ought to help tie together what's in the previous section.
</P
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN319"
></A
>5.1. Apache + mod_auth_pam</H2
><P
> As our example, we'll install and configure mod_auth_pam, an Apache module that allows you to authenticate users of your webserver using PAM. For the purpose of this example, I'll assume you have apache installed. If it's not installed already you should be able find installation packages from your distributor.
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN322"
></A
>5.2. Our example</H2
><P
> Our goal will be to configure a restricted area of our webserver, a <TT
CLASS="FILENAME"
>family/</TT
> directory, to authenticate users via PAM. This directory contains private family information, and should only be accessible to members of the user group family.
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN326"
></A
>5.3. Installing mod_auth_pam</H2
><P
> First, you'll want to download mod_auth_pam from <A
HREF="http://blank.pages.de/pam/mod_auth_pam/"
TARGET="_top"
>http://blank.pages.de/pam/mod_auth_pam/</A
>. The following commands will compile mod_auth_pam (you must be logged in as root):
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> <TT
CLASS="PROMPT"
>~#</TT
> <TT
CLASS="USERINPUT"
><B
>tar xzf mod_auth_pam.tar.gz</B
></TT
>
<TT
CLASS="PROMPT"
>~#</TT
> <TT
CLASS="USERINPUT"
><B
>cd mod_auth_pam-1.0a</B
></TT
>
<TT
CLASS="PROMPT"
>~/mod_auth_pam-1.0a#</TT
> <TT
CLASS="USERINPUT"
><B
>make</B
></TT
>
<TT
CLASS="PROMPT"
>~/mod_auth_pam-1.0a#</TT
> <TT
CLASS="USERINPUT"
><B
>make install</B
></TT
>
</PRE
></FONT
></TD
></TR
></TABLE
><P
> If you have any trouble installing the mod_auth_pam module, make sure you've installed your distribution's apache-dev package. After you've installed mod_auth_pam, you'll need to restart apache. Apache can usually by restarted by typing the following command (again, you must be root):
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> <TT
CLASS="PROMPT"
>~#</TT
> <TT
CLASS="USERINPUT"
><B
>/etc/init.d/apache restart</B
></TT
>
</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN343"
></A
>5.4. Configuring PAM</H2
><P
> The PAM configuration for Apache is stored in <TT
CLASS="FILENAME"
>/etc/pam.d/httpd</TT
>. The default configuration (which was installed when you installed mod_auth_pam) is secure, but it uses a module (<TT
CLASS="FILENAME"
>pam_pwdb.so</TT
>) which may not be available on many systems. (Besides, configuring it from scratch will be fun!) So delete the <TT
CLASS="FILENAME"
>/etc/pam.d/httpd</TT
> file, and start with a fresh one.
</P
><DIV
CLASS="SECT3"
><HR><H3
CLASS="SECT3"
><A
NAME="AEN349"
></A
>5.4.1. Deciding how to configure PAM</H3
><P
> If we're going to configure how PAM deals with Apache's authentication requests, we need to figure out exactly what we need PAM to check for. First, we want PAM to make sure the user's password matches their password in the standard unix password database. This sounds like the 'auth' type and the <TT
CLASS="FILENAME"
>pam_unix.so</TT
> module. We'll want the module's control type to be set to 'required', so authentication will fail without a correct password. Here's what the first line of our <TT
CLASS="FILENAME"
>/etc/pam.d/httpd</TT
> looks like:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> auth required pam_unix.so
</PRE
></FONT
></TD
></TR
></TABLE
><P
> Secondly, we must make sure that the users account is valid (i.e. their password has not expired or any such nastiness). This is the 'account' type and is also provided by the <TT
CLASS="FILENAME"
>pam_unix.so</TT
> module. Again, we'll set this module's control type to 'required'. After adding this line, our <TT
CLASS="FILENAME"
>/etc/pam.d/httpd</TT
> configuration looks like this:
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> auth required pam_unix.so
account required pam_unix.so
</PRE
></FONT
></TD
></TR
></TABLE
><P
> It's not terribly sophisticated, but it does the job. It ought to be a good start for learning how to configure PAM services.
</P
></DIV
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN360"
></A
>5.5. Configuring Apache</H2
><P
> Now that PAM is configured to authenticate apache's requests, we'll configure apache to properly utilize PAM authentication to restrict access to the <TT
CLASS="FILENAME"
>family/</TT
> directory. To do so, add the following lines to your <TT
CLASS="FILENAME"
>httpd.conf</TT
> (usually stored in <TT
CLASS="FILENAME"
>/etc/apache/</TT
> or <TT
CLASS="FILENAME"
>/etc/httpd</TT
>):
</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
> &#60;Directory /var/www/family&#62;
AuthPAM_Enabled on
AllowOverride None
AuthName "Family Secrets"
AuthType "basic"
require group family
&#60;/Directory&#62;
</PRE
></FONT
></TD
></TR
></TABLE
><P
> You may need to replace <TT
CLASS="FILENAME"
>/var/www/</TT
> with the default location of web documents, which is often <TT
CLASS="FILENAME"
>/home/httpd/</TT
>. Wherever that is, you'll need to create the <TT
CLASS="FILENAME"
>family</TT
> directory.
</P
><P
> Before we test our setup, I'll take a moment to explain the Apache configuration you just entered. The &#60;Directory&#62; directive is used to encapsulate configuration data for this directory. Inside this directive, we've enabled PAM authentication ("AuthPAM_enabled on"), turned off any overriding of this configuration ("AllowOverride none"), named this authentication zone "Family Secrets" ("AuthName "Family Secrets""), set the http authentication (not the PAM authentication) type to the default ("AuthType "basic""), and required the user group family ("require group family").
</P
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN373"
></A
>5.6. Testing our setup</H2
><P
> Now that we've got everything setup up properly, it's time to revel in our success. Fire up your favorite web browser and head over to http://your-domain/family/ (replacing your-domain with, well, your domain). You are now an uber-authenticator!
</P
></DIV
></DIV
><DIV
CLASS="SECT1"
><HR><H1
CLASS="SECT1"
><A
NAME="AEN376"
></A
>6. Resources</H1
><P
> There are a number of resources, both online and offline, where you can more information about user authentication. If you know of any resources that ought to be added to this list, drop me a line at <TT
CLASS="EMAIL"
>&#60;<A
HREF="mailto:petehern@yahoo.com"
>petehern@yahoo.com</A
>&#62;</TT
>
</P
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN380"
></A
>6.1. PAM</H2
><P
></P
><UL
><LI
><P
> <A
HREF="http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/pam.html"
TARGET="_top"
>Linux-PAM System Administrator's Guide</A
>
</P
></LI
><LI
><P
> <A
HREF="http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/pam_modules.html"
TARGET="_top"
>Linux-PAM Module Writer's Manual</A
>
</P
></LI
><LI
><P
> <A
HREF="http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/pam_modules.html"
TARGET="_top"
>Linux-PAM Application Developer's Manual</A
>
</P
></LI
></UL
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN392"
></A
>6.2. General Security</H2
><P
></P
><UL
><LI
><P
> <A
HREF="http://www.linuxsecurity.com/"
TARGET="_top"
>linuxsecurity.com</A
>
</P
></LI
><LI
><P
> <A
HREF="http://www.securitywatch.com"
TARGET="_top"
>securitywatch.com</A
>
</P
></LI
><LI
><P
> <A
HREF="http://www.linuxdoc.org/HOWTO/Security-HOWTO.html"
TARGET="_top"
>Security HOWTO</A
>
</P
></LI
><LI
><P
> <A
HREF="http://packetstorm.securify.com"
TARGET="_top"
>Packetstorm</A
>
</P
></LI
></UL
></DIV
><DIV
CLASS="SECT2"
><HR><H2
CLASS="SECT2"
><A
NAME="AEN407"
></A
>6.3. Offline Documentation</H2
><P
> A lot of information can be gathered from your system's manual pages. The following are some manpages relating to user authentication. The number in parentheses refers to the manpage section. To view the passwd(5) manpage, you would enter <TT
CLASS="USERINPUT"
><B
>man 5 passwd</B
></TT
>.
</P
><P
></P
><UL
><LI
><P
> <SPAN
CLASS="CITEREFENTRY"
><SPAN
CLASS="REFENTRYTITLE"
>passwd</SPAN
>(5)</SPAN
>
</P
></LI
><LI
><P
> <SPAN
CLASS="CITEREFENTRY"
><SPAN
CLASS="REFENTRYTITLE"
>crypt</SPAN
>(3)</SPAN
>
</P
></LI
><LI
><P
> <SPAN
CLASS="CITEREFENTRY"
><SPAN
CLASS="REFENTRYTITLE"
>pam.d</SPAN
>(5)</SPAN
>
</P
></LI
><LI
><P
> <SPAN
CLASS="CITEREFENTRY"
><SPAN
CLASS="REFENTRYTITLE"
>group</SPAN
>(5)</SPAN
>
</P
></LI
><LI
><P
> <SPAN
CLASS="CITEREFENTRY"
><SPAN
CLASS="REFENTRYTITLE"
>shadow</SPAN
>(5)</SPAN
>
</P
></LI
></UL
></DIV
></DIV
><DIV
CLASS="SECT1"
><HR><H1
CLASS="SECT1"
><A
NAME="AEN437"
></A
>7. Conclusion</H1
><P
> I hope you found this HOWTO helpful. If you have any questions, comments, or suggestions, I'd love to hear from you. You can email me at <TT
CLASS="EMAIL"
>&#60;<A
HREF="mailto:petehern@yahoo.com"
>petehern@yahoo.com</A
>&#62;</TT
>.</P
></DIV
></DIV
></BODY
></HTML
>