NAME

class::Htpasswd - Manage Unix crypt-style password file.


SYNOPSIS

include("class.Htpasswd.php3");

$Htpasswd = new Htpasswd("/home/users/cdi/.htpasswd");

$Htpasswd->addUser("cdi","secret");

$Htpasswd->changePass("cdi","new-password","secret");

$Htpasswd->verifyUser("cdi","new-password");

$Htpasswd->getPass("cdi");

$Htpasswd->deleteUser("cdi");

$Htpasswd->renameUser("OldName","NewName","Password");


DESCRIPTION

This class facilitates the maintenance of htpasswd files, used to do Basic Authentication on a web server. This class assumes PHP v3.0 or better and Apache 1.3.3 or better.

The password file is a flat-file with login name and their associated crypt.3 passwords, in the format login:password.


METHODS

new("/full/path/to/htpasswd/file" );

Creates a new Htpasswd object. The path and file may be omitted. If the path and file are omitted, the initialize() method must be called. "path-to-file" should be the path and name of the file containing the login/password information.

initialize("/full/path/to/htpasswd/file" );

The first method that should be called. (if file was omitted from the new() method) Loads the file specified into memory. Some methods may be called or some variables set prior to initialize() to modify how it reads the file. See also sane(), and do_not_blame_cdi(). "path-to-file" should be the path and name of the file containing the login/password information.

verifyUser("user id", "unencrypted password" );

Given a user id and password, returns true if the password matches the encrypted version stored in the htpasswd file. Returns false otherwise.

isUser("user");

Given a user id, will return true if that ID exists in the password file, will return false otherwise. It should be noted that user ids are case sensitive, so user is NOT the same user ID as User or USER.

getUserNum("user");

Given a user id, will return the user (line) number of that user. All entrys in the password file are assigned the User Id of the USERS() array index. All of this is just glorified line numbers from the password file, where line number 1 is user number 0. The total number of users in the password file can be obtained from the USERCOUNT variable. Returns null if the user is not found.

version();

Returns the version information for this class.

do_not_blame_cdi();

Used to turn off sanity checking on the file pointed to by new() or initialize(). This is not reccommended. See also sane().

getPass("user");

Given a userID, will return the user's encrypted password from the password file. Returns null if the user is not found.

changePass("user", "newPass", "oldPass");

changePass() will change the password of an existing user. If "oldPass" is omitted or is empty/null, a password change is forced. If the "oldPass" is submitted, changePass() first verifies that the password they entered was correct before it will change their password. "newPass" should be the plain text version of the user's password, as this method will automatically encrypt it. Returns true upon success, returns false if their "oldPass" doesn't match or if the user wasn't found in the password file.

renameUser("OldName", "NewName", "password");

renameUser() changes a user ID in the password file. If the "password" is empty/null, the name change is forced. If the password is sent, the "OldName" is first authenticated prior to allowing the name change. If "OldName" doesn't exist or if "NewName" does exist, the change fails. Returns true on success, false on failure.

addUser("user", "password");

addUser() will add a user and password to the password file. The password given to this method must not be encrypted as the addUser() method will first encrypt the password, then save it. You do not need to concern yourself with generating a salt for the password encryption, as this method automatically calls genSalt(). Returns true on success, false on failure, and the script exits with a fatal ERROR condition if it fails to save the new password file. Will also return false if the user id submitted already exists in the password file.

genSalt();

Called directly, genSalt() will return a random two digit salt. The salt will have the range A-Za-z./, which are the only legal characters that can be used by crypt.3. This method is considered internal, as all password encryption routines will generate their own random salts automatically.

genPass();

If you want to generate a random password, which is always a better idea than letting users pick wimpy passwords, use the genPass() method. This method returns a randomly generated 5 to 8 character plain text password, which can then be sent to addUser() or changePass() as the users password. The password generated is in the range [A-Za-z0-9!@#$%^&*()-=_+].

assignPass("user");

assignPass(UserID) adds the user to the password file using an automatically generated password. This method returns the plain text version of a genPass() password. This method is designed to quickly add users to the password file while maintaining the highest degree of security by forcing random passwords on the user. It should be noted that when this method returns the password, the user has already been added to the password file. This method returns null upon failure. A file write error will cause the program to immediately exit.

genUser();

To really clamp down on your system security, you can also assign random user IDs. genUser() returns a random 5 to 8 character user ID, suitable for use with assignPass(). IDs generated will be in the range A-Za-z0-9.

deleteUser("user");

Fairly straight forward, deleteUser() removes a given user from the password file. Returns true on success, false on failure.

getUserNum("user");

All users in the password array have a user number assigned to them, corresponding to their line number from the password file. getUserNum() returns the users ID, or -1 on failure. Remember, this is a zero based array where line number one from the password file will be userNum zero. See also USERCOUNT.

cryptPass("password","salt");

Returns the encrypted form of password using the submitted salt. If the salt is omitted or is null, cryptPass() will automatically generate a random salt before encrypting the password. This method is considered internal, as the higher level methods that encrypt user passwords will automatically call this method.

htWriteFile();

This is an internal method and is called anytime the password file needs to be written. If however, you have made your own changes to the USERS array, calling this method will save those changes to the file. Returns true on success, false upon failure. If for any reason htWriteFile() cannot save the file, it will immediately echo an error message to the client and exit the program. If htWriteFile() is successfull, it automatically re-calls the initialize() method and refreshes the file contents in memory. Word to the wise: Since the class loads the file contents into memory, if multiple processes update the file, strange things may happen. I've tried to make it so that the class makes no assumptions about the file's contents, and the way it writes the file should be safe from multiple hits to it simultaneously. I can't guarantee that though. The PHP maintainers really need to implement some kind of file locking routines. If you're concerned that this may become a problem, you'll need to implement some kind of locking procedure within your own program. Some pseudo-code on how you might do this;

   +--+-  [check for a lock]
   |  |   |
   |  |   +------ No lock -> initialize()
   |  |   |                            |
   |  |   |                            +- lock file
   |  |   +-- Locked --+                   |
   |  |                |                   +- Do Stuff
   |   +------<--------+                        |
   |                                            +- Unlock
   |                                                 |
   + ------------------<-----------------------------+


VARIABLES

The following global variables are available after an initialize() method is invoked. Some may be set before the initialize() method to effect the results. (DEBUG and the method do_not_blame_cdi() )

ERROR (string)
If a method returns an error, or a method fails, the error can be retrived by looking at this variable for the error text.

DEBUG (boolean)
Turns logging of errors on or off, default is true.

FILE (string)
Contains the path and file name submitted by the last new() or initialize() method.

EXISTS (boolean)
Set to true if the file pointed to by initialize() or new() exists. If starting a new password file, will remain false until the first user is added to the file, then will be true from then on out. (Or until all users are deleted from the file)

EMPTY (boolean)
Set to true if the file EXISTS but contains no data.

SANE (boolean)
Set to true if the password file is sane. False otherwise. Disable sanity checking by calling do_not_blame_cdi() before calling the initialize() method. If you want to turn off sanity checking, you must call the initialize() method after calling the do_not_blame_cdi() method.

VERSION (string)
The version of this class file.

CONTENTS (possibly really big string)
The raw file contents as one big scalar. Allows you to perform your own functions upon the raw data. Changes made to CONTENTS are not written to the password file. You must modify the contents of the USERS array and then call htWriteFile() if you want changes made to the actual file contents.

USERCOUNT (integer)
The total number of users (lines) in the password file. Remember, user number 1 is USERCOUNT 0, so the actual total number of users in the file is USERCOUNT - 1.

USERS (array)
The user database array after a successful initialize() method. The array is in the form of:

$Htpasswd->USERS[index number][ "user" ] = Username

$Htpasswd->USERS[index number][ "pass" ] = Password

So, to grab the username for UserID # 5:

$User5 = $Htpasswd->USERS[ 5 ][ "user" ];

WIN32 (boolean)
Set this to true if the script is on a Win32 box. Will prevent the class from performing ``unix only'' stuff.

ERRORS

Most of the time, the contents of the last error can be obtained by grabbing the ERROR variable. As in

 echo " oops, $Htpasswd-E<gt>ERROR \n " ; 
.

Only one type of error is not trapped in this fashion: Fatal file access errors, specifically fatal file write errors. If at any time this class cannot write a file it needs to write, it will immediately exit the entire program, rather than compromise security by assuming the file was successfully written. The class will echo the contents of ERROR to the client prior to exiting.

EXAMPLES

See the example files in the examples/ directory, created by this archive when it was uncompressed.

INSTALLATION

You install class.Htpasswd.php3, as you would install any PHP library, just copy it to the directory pointed to by include_path in your php3.ini file:

cp class.Htpasswd.php3 $INCLUDE_PATH/class.Htpasswd.php3

chmod 444 $INCLUDE_PATH/class.Htpasswd.php3

Change $INCLUDE_PATH to whatever your include_path directive is set to. If you don't have include_path set, then edit your php3.ini file and set one. That's it. Simple yes?

DOCUMENTATION

Other than this man page, you're on your own. Created using POD style documentation and manified by a custom version of pod2man. HTML documentation created by a modified verison of pod2html.

The comments in the source code itself should also help, but I'm not guaranteeing that.

AVAILABILITY

The latest version of class.Htpasswd.php3 should always be available from:

http://www.thewebmasters.net/php/Htpasswd.phtml

or

http://php.codebase.org/

VERSION

Revision 0.9 1999/01/27 16:41:00

CHANGES

Added the renameUser() method per request from Rainer Scholz jrs@startrek.franken.de . General code clean-up.

Coming soon: class.Htgroup.php3

BUGS

None, and you better not find any either.

RESTRICTIONS

May have problems on Win32 platforms. If you want it to work on Win32 then you can bloody well make the changes. (And get credit for it in the official distro if you're nice enough to send me your changes)

If running PHP as a CGI: Be sure to set DEBUG to false, or modify the error() function to log to someplace other than error_log().

FATAL errors during file operations will cause an immediate exit of the program. Fatal errors are echo'd to STDOUT.

FILES

class.htpasswd.php3, and this one Htpasswd.php.3

SEE ALSO

Port inspired by the Apache::Htpasswd perl module by Kevin Meltzer, kmeltz@cris.com. (Available from CPAN http://www.cpan.org )

htpasswd.1, httpd.8, and crypt.3

AUTHOR INFORMATION

Copyright 1999, CDI cdi@thewebmasters.net. All rights reserved. It may be used and modified freely, but this copyright notice must remain attached to the file (or modified versions of this file). You may modify this source code as you wish, but if you redistribute a modified version, you must attach a note listing the modifications you have made and you must send me the source. If your changes or modifications warrant they will be included in the next official revision.

Address bug reports and comments to:

cdi@thewebmasters.net

The author makes no warranties, promises, or gaurantees of this software. As with all software, use at your own risk. This software is distributed "AS-IS".