user_manage handles most variants of the Apache "Basic" and "Digest" authentication scheme. Using one simple interface, it manages each of the following types of password and group files:
Many of user_manage's functions will also work with other Unix Web servers, including NCSA httpd, and the CERN server. There is support for Netscape user (but not group) databases. This script has not been tested with Windows NT servers.
This will unpack the distribution in a directory named HTTPD-User-Manage-X.X, where X.X is the current version number.% zcat user_manage.tar.gz | tar xvf - drwxr-xr-x lstein/lstein 0 Dec 22 20:14 1997 HTTPD-User-Manage-1.5/ drwxr-xr-x lstein/lstein 0 Dec 22 20:14 1997 HTTPD-User-Manage-1.5/lib/ drwxr-xr-x lstein/lstein 0 Dec 22 20:14 1997 HTTPD-User-Manage-1.5/lib/HTTPD/ drwxr-xr-x lstein/lstein 0 Dec 22 20:14 1997 HTTPD-User-Manage-1.5/lib/HTTPD/UserAdmin/ -r--r--r-- lstein/lstein 1934 Dec 22 18:49 1997 HTTPD-User-Manage-1.5/lib/HTTPD/UserAdmin/Text.pm -r--r--r-- lstein/lstein 5847 Dec 11 17:30 1997 HTTPD-User-Manage-1.5/lib/HTTPD/UserAdmin/SQL.pm ...
This will copy the HTTPD modules to the correct location within the Perl library directory. You may need to be root in order to do this.% perl Makefile.PL % make % make test % make install
The HTTPD modules are useful in their own right. During the installation process, Perl will have created man pages for them which you can read with the perldoc command. Read the manual page for HTTPD::Realm first, then HTTPD::RealmManager.
#!/usr/local/bin/perl
Several workable configurations are possible:
Designate a directory that will hold the various password and group files, for example /etc/httpd/security. Make it owned and writable by a specially-designated "web administrator" account, for example "www". Now, running as root, change the ownership of user_manage to "www" and set its "s" bit:
In order for suid to work, this feature needs to be configured into Perl when it is compiled. You may need to reinstall Perl if suid was not enabled, particularly if Perl came preinstalled on your system. There is also a bug in perl 5.003 when running under some versions of OSF/1 (Digital Unix) that causes suid scripts to exit silently without producing any output. To fix this bug, apply the patch located in the file suid.patch included with this distribution to the Perl source tree and recompile.# chown www user_manage # chmod u+s user_manage # ls -l user_manage -rwsr-xr-x 1 www users 18928 Jan 14 03:47 user_manage*
Under this scheme it may be convenient to have the script create new password and group files with group writable permissions. To do this, change the script global $CREATE_MODE to "0664". (See Configuring the Script.# chgrp www user_manage # chmod g+s user_manage # ls -l user_manage -rwxr-sr-x 1 root www 18928 Jan 14 03:47 user_manage*
While this strategy is effective, it has the disadvantage that it gives the web server and all other CGI scripts the ability to modify the password and group files. A remote user exploiting a security hole in the server or one of its CGI scripts could then take advantage of this fact to alter the password file.
$CONFIG_FILE
$ADMIN_GROUP
$PROTECT_ADMINS
$DEFAULT_GROUP
$REQUIRE_ACCESS_CONTROL
With this variable set to a non-zero value, the script will refuse to run at all unless it is in a password-protected directory. Both GET and POST methods must be restricted. Here's a typical excerpt from Apache's access.conf file:
Placing the script under access control provides a small amount of additional security at the cost of an extra layer of complexity. It is ordinarily unnecessary.<Location /cgi-bin/admin> AuthUserFile /etc/httpd/security/passwd AuthGroupFile /etc/httpd/security/group AuthType Basic AuthName Main <Limit GET POST> order allow,deny allow from all require valid-user </Limit> </Location>
$CREATE_MODE
$STTY
$CONFIG_FILE
global.
A sample configuration file, realms.conf is included in this distribution. Its format is similar to that used by Apache for its access control file. Blank lines and lines beginning with the "#" character are ignored. The file should contain a series of realm definitions, each beginning with a <Realm> directive and ending with a </Realm> directive. For example:
This example shows the definitions for two security realms, one named "main" and the other named "development". Each realm section contains the three directives Users, Groups, and Type:<Realm main> Users /etc/httpd/security/passwd Groups /etc/httpd/security/group Type Text </Realm> <Realm development> Users /etc/httpd/security/devel.passwd Groups /etc/httpd/security/devel.group Type DBM </Realm>
Directive | Example Parameters | Description |
---|---|---|
Users | /etc/httpd/passwd | Path to the user password file |
Groups | /etc/httpd/group | Path to the group file |
Type | DBM | Type of file to use |
The Users and Groups directives should contain absolute path names (except when using SQL databases for authentication; see below). Relative path names will be rejected. Type may be one of Text, DBM, DB, or SQL. These correspond to Apache's flat file, DBM file, DB file, and mSQL authentication formats respectively. Please be careful that the user and group files that you declare in Apache's access.conf file correspond to the type declared in the user_manage configuration file:
Type | Password File Directive | Group File Directive |
---|---|---|
Text | AuthUserFile | AuthGroupFile |
DBM | AuthDBMUserFile | AuthDBMGroupFile |
DB | AuthDBUserFile | AuthDBGroupFile |
SQL | see below | see below |
The first realm defined in the configuration file becomes the default, unless otherwise specified by a Default directive (see the next section).
Directive | Example Param | Description |
---|---|---|
Authentication | Basic | Authentication scheme |
Database | www@capricorn.com | Location of SQL db |
Default | none | Default realm |
Driver | mSQL | DBI SQL driver |
Fields | name age paid | Additional user fields |
Groups | /etc/httpd/group | Path to group database |
GroupType | SQL | Database type |
Server | NCSA | Type of server |
Type | DBM | Database type |
Users | /etc/httpd/passwd | Path to user database |
UserType | DB | Database type |
The Fields directive specifies a list of field names of the form name[:type][width]. Only the name is required. The field type and width are optional hints that help user_manage format the field values correctly on the Web page. The type can be one of "i", for an integer value, "s" for a string value and "f" for a floating point number. If not specified, the field is assumed to be of type string. The field value must be an integer.
In the example below we define three fields named "Name", "Age" and "Paid". The first is a string value of default length. The second is an integer. The third is a string of length one (it's assumed to be a "Y" or "N"):
Fields Name Age:i Paid:s1Many more fields may be present in SQL databases than are listed in the Fields directive. The Fields directive just tells the user_manage script which fields should be made visible to the user interface.
Server cernIf no server is specified, "apache" is assumed. If your server is not on this list, try "ncsa".
When using SQL databases, the Users and Groups directives in the realms configuration file have a different format. Instead of pointing to a physical file, these directives contain information about what table and field the user, password and group information is stored in. A typical Users directive looks like this:
The directive consists of three tags. The "table" tag designates the database table that contains the user information. "uid" indicates the field that contains the user name, and "password" names the field that contains the user's password. For performance reasons, the user name field should be declared the primary index.Users table=users uid=name password=pass
The Groups directive has a similar structure:
"table" contains the name of the database table in which the group information can be found. "group" contains the name of the field in which the group name is stored. You do not have to provide a "uid" tag in this case, because it is automatically inherited from the uid field in the Users directive. If you do provide a "uid" tag, it must be the same as the corresponding field in the users table (this is an Apache limitation; not a user_manage limitation). Note that ANSI SQL forbids you from using the word "group" as a field name. Some databases, including mySQL will not allow you to create a table with a column named "group."Groups table=groups group=grp
A typical mSQL user table looks like this:
users +--------------+---------------+----------------------+------+------+ | name | password | full_name | age | paid | +--------------+---------------+----------------------+------+------+ | agnes | nLMOlUUs0/3GM | Agnes Smith | 20 | Y | | lstein | 85yefVEyK06Is | Lincoln Stein | 37 | Y | | phillip | y/iH4JfAbyLS2 | Phillip Smith | 19 | N | | wanda | xKEzzIfvHdMug | Wanda Smith | 19 | Y | +--------------+---------------+----------------------+------+------+A typical groups table looks like this:
groups +----------------------+--------------------------------+ | name | grp | +----------------------+--------------------------------+ | agnes | users | | agnes | authors | | agnes | engineers | | lstein | administrators | | lstein | authors | | phillip | users | | wanda | users | +----------------------+--------------------------------+In this example, agnes belongs to groups named "users," "authors," and "engineers." lstein belongs to "authors" and "administrators," while phillip and wanda each belong to "users" only.
You may use the same table for both users and their groups. However if you want to assign a user to more than one group, then you must use separate tables. This is because each user record must be unique in the user table, whereas the same user will appear once in the group table for each group that he or she belongs to. Another implication of this is that the user ID field in the group table cannot be a primary key, otherwise it would be forced to be unique.
As in the Fields directive, you can provide optional field widths for each of the field declarations in the Users and Groups directives. The field widths are used as hints by the CGI script version of user_manage to format the fill-out form nicely. Follow the field name with a colon and the field width, as in:
There is no need to declare the field type since they must be strings, but it doesn't hurt to do so.Users table=users uid=name:30 password=pass:13
You will also need to declare the location of the database and its DBI driver using Database and Driver directive. If these directives do not appear, they default to "www@localhost" and "mSQL" respectively.
Unlike other database types, user_manage does not automatically create SQL database tables for you. You will need to create the tables manually before you use user_manage for the first time. If you wish, the user_manage "setup" command will output the necessary SQL table creation commands for you.
<A HREF="/cgi-bin/user_manage?realm=development"> Change Your Password </A>
This link invokes the user_manage script with the single parameter realm set to the security realm you wish to modify. If you don't provide this argument, the first realm defined in the script's configuration file will be assumed.
To create a button that does the same thing, adapt the following fragment of HTML:
When the user selects this link, she will be presented with a page similar to the one shown here, which prompts her to type in her name and password.<FORM ACTION="/cgi-bin/user_manage" METHOD=POST> <INPUT TYPE="hidden" NAME="realm" VALUE="development"> <INPUT TYPE="submit" VALUE="Change Password"> </FORM>
After the script confirs that the user's name and correct password are found in the password file, the user will be prompted to enter a new password with a page like the one shown here:
The user enters her new password twice in order to avoid typing errors. The script then confirms that the password was successfully changed.
If an error occurred while updating the password file, the user will be given a generic error message. A more specific error message containing diagnostic output will be found in the server's error log.
$ADMIN_GROUP
global), a different
screen will appear similar to the screenshot shown here. This screen
contains a pop-up menu of all currently defined users (it turns into a
scrolling list when the number of user exceeds eight). It also
contains a blank textfield for adding entirely new users. From this
screen you can edit the passwords and groups of existing users, define
new users, and delete users entirely.
The checkboxes labeled Set Groups contains all the groups currently defined. Toggle all the groups that you wish the user to belong to. If a group isn't already defined, you can type in its name in the text field labeled Other:. Enter and reenter the password for this user in the text fields labeled Password Enter and Confirm. When you are satisifed, press the Set Values button. A confirmation will appear at the top of the page, and the script will display the "User Edit" page described in the next section. You can re-edit the user (to define new groups, for example), edit a different user, or add another new user.
If any additional user fields are defined in the configuration file, they will appear as a series of text fields in a section labeled "Other Information". In the example shown here there are three labeled "name", "age" and "paid."
If there are more than five groups defined, the list of checkboxes will turn into a scrolling list in order to save space.
The screenshot below also shows how the popup menu of existing users turns into a scrolling list when the number becomes large.
The syntax for invoking the script from the command line is:
user_manage [realm] command argument1 argument2 argument3The first argument to the command is the security realm. You can, if you wish, omit the realm name entirely. If you do so, the script will default to the first realm defined in its configuration file. This is followed by a command indicating the action you want the script to take. Following this are zero or more additional arguments, the meaning of which depend on the command that's being issued.
Here's a summary of the commands and their arguments:
Command | Arguments | Description |
---|---|---|
setup | (none) | First-time setup for a database |
add | user password [group1,group2...] [field1=value1,field2=value2...] | Add or edit a user's password & groups |
delete | user1 user2... | Delete named users |
edit | user password [group1,group2...] [field1=value1,field2=value2...] | A synonym for "add" |
realms | (none) | Concise list of all defined realms |
group | user [group1,group2...] | Set the user's groups |
group | user [field1=value1,field2=value2...] | Set the user's values |
view | user1 user2... | View users' entries |
view | (none) | View all users |
format | (none) | Create a formatted entry for access.conf |
If the database already exists, its other user entries will not be erased. However, if the name you chose for the administrative account already exists in the database it will be overwritten.$ user_manage -r development setup Pick a name for the administrative group [administrators]: Pick a name for the administrative account: lstein New password: ******** Re-type new password: ******* Added lstein to database development in group administrators.
If you are using an SQL database, "setup" will not make any changes directly to the database. Instead, it prints out a series of SQL commands suitable for feeding to the database via a command-line or batch tool. For example:
$ user_manage -r wizards setup Pick a name for the administrative group [administrators]: Pick a name for the administrative account: lstein New password: ****** Re-type new password: ****** Create database www and feed it this code: CREATE TABLE users ( uid char(12) primary key, password char(3) not null, name char(30), age int, paid char(1) )\g INSERT INTO users (uid,password) VALUES('lstein','TTvO6DRt2phqc')\g CREATE TABLE groups ( uid char(12), grp char(20) )\g INSERT INTO groups (uid,grp) VALUES('lstein','administrators')\g
Note that the passwords appear in encrypted form. All passwords are encrypted before being placed into the password file. Once encrypted, there is no easy way of recovering the original password.$ user_manage -r development view Name Password Groups Info ---- -------- ------ ---- ben Yr.a3p6C/3Rlg users ebuliah DZlBiw41ZDmo2 users joshua 9Fb2NkZe4xY4I authors,users name=Joshua Canaan,age=20,paid=Y leigh krIfoqNKkpyho users name=Leigh Deacon,age=28,paid=Y lois aBIdyi3Q2FdTw users name=Lois Lane,age=24,paid=Y lstein xfPti/KeINcC2 administrators,authors, users $ user_manage development view lois joshua Name Password Groups Info ---- -------- ------ ---- joshua 9Fb2NkZe4xY4I authors,users name=Joshua Canaan,age=20,paid=Y lois aBIdyi3Q2FdTw users name=Lois Lane,age=24,paid=Y
$DEFAULT_GROUP
global variable. If
you specify an empty group using any of "-", '' or "", the user will
be assigned to no groups.
If the realm has additional fields defined by a Fields directive, you can set this information here by passing user_manage a comma-delimited series of field name = value pairs. Be careful if field values contain embedded white space or shell metacharacters. If this is the case, you'll need to protect the entire series of name=value pairs with quotation marks. Field names that are not explicitly listed in the configuration file will be silently ignored.
If you do not enter the user name or password on the command line, you will be prompted for it. It's actually safer not to type passwords on the command line as they can be easily intercepted by other users of the system.
In each of these examples, the realm has been omitted, allowing user_manage to use the default realm.
Add a user and password, accepting default group assignment $ user_manage add joseph open-sesame Password successfully changed for joseph. Group set to users. Add a new user and password, setting groups explicitly $ user_manage add leigh greenman users,authors Password successfully changed for leigh. Group set to users authors. Add a new user and password, setting groups and extra info $ user_manage add george xyzzy users,authors "name=George Jetson,paid=Y" Password successfully changed for george. Group set to users authors. Let the script prompt for missing values $ user_manage add User name: duncan Enter password: Re-type password: Password successfully changed for duncan. Group set to users.
Example:
If you omit the user name or list of groups, the program will prompt you for their values.$ user_manage group joseph users,authors,administrators Groups set for joseph.
Example:
If you don't specify the user name, the program will prompt for it.$ user_manage info george age=20 Info successfully changed for george. $ user_manage info george Enter comma-separated list of field=value pairs for george: age=80 Info successfully changed for george.
The default realm is indicated with an asterisk.$ user_manage realms Name Type ---- ---- development DB *main File test DBM
Here are some examples:
This can be a real time-saver, particularly if you have trouble remembering the spelling of all those directives!$ user_manage -r development format AuthName development AuthType Basic AuthDBUserDB ./devel.passwd AuthDBGroupDB ./devel.group <Limit GET POST PUT DELETE> require valid-user </Limit> $ user_manage -r wizards format AuthName wizards AuthType Basic Auth_MSQLHost localhost Auth_MSQLDatabase www Auth_MSQLpwd_table users Auth_MSQLuid_field uid Auth_MSQLpwd_field password Auth_MSQLgrp_table groups Auth_MSQLgrp_field grp <Limit GET POST PUT DELETE> require valid-user </Limit>
The one exception to this rule is that if the user's UNIX login name matches the name of a user who belongs to the "administrators" group, the script will honor the sgid and suid bits. This mimics the behavior of the script when it's running as a CGI program.
The file locking used by this script is based on the Unix fcntl() call, which does not work correctly across NFS mounted volumes. Because of this, the password and group files must reside on one of the web server's local disks. For the same reason, if you use user_manage from the command line, you should do it from the web server's host machine.
If the script exits prematurely for some reason, it may leave lock files lying around. These appear as files with the extension ".TMP" in the directory holding the password and group files. You can safely ignore these files or delete them. Their presence will not adversely affect the script's operation. If the script consistently fails to clean up lock files, please contact me. There's probably a bug.
Another thing to be aware of is that the Apache server itself doesn't honor the file locks. If a user updates a password or group file at the same time that Apache is trying to read it, the server may read partial or outdated information. This may cause rare intermittent user authentication errors. (The same is also true of the htpasswd and dbmmanage scripts, which don't use any sort of locking at all).
An Apache module that works with Doug MacEachern's mod_perl is in the works to correct this deficiency
http://www.genome.wi.mit.edu/~lstein/user_manage/If you are having trouble with the script, check here for updates.
user_manage was written by Lincoln D. Stein. It can be freely distributed and modified, so long as this documentation accompanies it and the following copyright statement is displayed in the source code:
Copyright 1997-2000 Lincoln D. Stein. All rights reserved. See the accompanying HTML file for usage and distribution information. The master version can be found at: http://www.genome.wi.mit.edu/ftp/pub/software/WWW/passwd/