In this article, you will:
- Compile the module
- Configure and use the module: the new directives, a practical sample, caching
- Compile the UDFs
Since the first release of this article, the Apache webserver has changed quite a bit and so has the module. Apache 2.2 introduced a new authentication backend provider scheme which explains the change of the module name for Apache versions >= 2.2. Although Apache 2.2 provides a SQL database API, called the DBD framework, it makes sense to use mod_authnz_ibmdb2, which allows you to cache the user and group information and it validates not only the hashes used by Apache but also regular MD5 hashes and plain text passwords. Regular "not seeded" MD5 hashes are very often used in web applications.
Active development of mod_auth_ibmdb2 for Apache 1.x has been stopped, but you can find the latest version for Apache 1.x in the new package 'mod_auth_ibmdb2 (Apache 1.x)' on sourceforge.net.
To begin, you will need access to a DB2 system. It does not matter if DB2 is installed on the same machine as Apache or if the database is located on another machine. If the database is located on another machine, make sure that the application development client is installed and configured on the machine that is running Apache. For information on installing DB2, please refer to Speed-start your Linux app 2003: Installing DB2 Version 8.1.
The module and the UDFs are written in C and should compile and run on all platforms that have the proper include files and libraries installed.
This article focuses on the Linux platform. The modules have been developed on Linux.
Download the latest source codes from the mod_auth(nz)_ibmdb2 Web site, which are at the time of this writing, mod_authnz_ibmdb2-1.26.tar.gz (Apache 2.2), mod_auth_ibmdb2-1.10.tar.gz (Apache 2.0) and db2-auth-udfs-1.3.tar.gz.
Open a terminal and log in as root. Next, change to a directory where you can extract the mod_authnz_ibmdb2-1.26.tar.gz. (in this example I will use the directory /ext).
[root@atvie01s ext]# tar -xzf mod_authnz_ibmdb2-1.26.tar.gz [root@atvie01s ext]# cd mod_authnz_ibmdb2 [root@atvie01s mod_authnz_ibmdb2]# ls -l total 80 -rw-r--r-- 1 tessus tessus 13521 2007-03-21 22:39 caching.h -rw-r--r-- 1 tessus tessus 778 2008-01-08 05:47 CHANGES -rw-r--r-- 1 tessus tessus 1514 2008-01-08 05:47 INSTALL -rwxr-xr-x 1 tessus tessus 940 2007-03-21 06:50 makemod -rwxr-xr-x 1 tessus tessus 993 2008-01-08 05:16 makemod.bat drwxr-xr-x 3 tessus tessus 16 2008-01-08 05:49 man -rw-r--r-- 1 tessus tessus 36003 2008-01-08 03:40 mod_authnz_ibmdb2.c -rw-r--r-- 1 tessus tessus 4022 2008-01-07 00:44 mod_authnz_ibmdb2.h -rw-r--r-- 1 tessus tessus 4218 2008-01-08 05:47 README |
You will find a makemod script within this directory that we will use to build the module (shared object) for Apache. For this you have to check some parameters in the script. Use your favorite editor for changing the following parameters to the appropriate values for your environment.
DB2PATH=/home/db2inst1/sqllib
APXS=/usr/local/apache/bin/apxs
Set DB2PATH to the directory where DB2 UDB is accessed. This is usually the instance home directory. Set APXS to the path that points to your apxs binary. The apxs binary is usually installed in the /<your apache home>/bin directory.
After changing the above settings, run the following script:
[root@atvie01s mod_authnz_ibmdb2]# ./makemod |
Next, add the following line to your httpd.conf:
Apache >= 2.2:
LoadModule authnz_ibmdb2_module modules/mod_authnz_ibmdb2.so |
Apache 2.0.x and Apache 1.3.x:
LoadModule ibmdb2_auth_module modules/mod_auth_ibmdb2.so |
You may also need to add the following line when using Apache 1.3.x:
AddModule mod_auth_ibmdb2.c |
If the sqlcli1.h header file or the DB2 library libdb2.so cannot be found, it is likely that you have not set the DB2PATH correctly. In this situation search your system for the two above-mentioned files.
With the module comes several new directives for Apache to control the behavior of the authentication process. Here is a list of the new directives, their default values and a description.
Table 1. Listing of directives
|
Directive
|
Default Value
|
Description
|
|---|---|---|
|
|
|
|
| AuthIBMDB2User |
| User for connecting to the DB2
database |
| AuthIBMDB2Password |
| Password for connecting to the
DB2 database |
| AuthIBMDB2Database |
| Database name |
| AuthIBMDB2UserTable |
| Name of the user table. If
not fully qualified, the user that was used to connect to the database
is used as the schema name. |
| AuthIBMDB2GroupTable |
| Name of the group table. If not fully qualified, the user that was used to connect to the database is used as the schema name. |
| AuthIBMDB2NameField | username | Name of the username column
within the user and group table |
| AuthIBMDB2GroupField | groupname | Name of the groupname column within the group table |
| AuthIBMDB2PasswordField | password | Name of the password column within the user table |
| AuthIBMDB2CryptedPasswords | on | If AuthIBMDB2CryptedPasswords is "On," then the passwords are stored encrypted. You do not have to specify what kind of encryption was used. Valid encryption methods are: normal md5 hash (32 chars as in php), seeded md5 value (as generated with Apache's htpasswd utility or as in /etc/shadow), crypt. If "Off," plain text passwords are used. For generating passwords within DB2, see the UDFs section in this article. |
| AuthIBMDB2KeepAlive | on | If AuthIBMDB2KeepAlive is "On,"
then the server instance will keep the IBMDB2 server connection open. |
| AuthIBMDB2Authoritative | on | If AuthIBMDB2Authoritative is
"Off," then iff the user is not found in the database, let other auth
modules try to find the user. |
| AuthIBMDB2NoPasswd | off | If AuthIBMDB2NoPasswd is "On,"
then any password the user enters will be accepted as long as the user
exists in the database. |
| AuthIBMDB2UserCondition |
| Can be used to restrict queries
made against the user table. The value should be a string that you want
to be added to the end of the where-clause when querying the table. For
example, if your user table has an "active" field and you only want
users to be able to login if that field is 1, you could use a directive
like this: AuthIBMDB2UserCondition active=1 |
| AuthIBMDB2GroupCondition |
| Can be used to restrict queries made against the group table. The value should be a string that you want to be added to the end of the where-clause when querying the table. For example, if your group table has an "active" field and you only want users to be able to login if that field is 1, you could use a directive like this: AuthIBMDB2GroupCondition active=1 |
| AuthIBMDB2UserProc |
| Name of the stored procedure for user authentication |
| AuthIBMDB2GroupProc |
| Name of the stored procedure for group authentication |
| AuthIBMDB2Caching | off | If AuthIBMDB2Caching is "On,"
the user credentials are cached in a file. |
| AuthIBMDB2GroupCaching | off | If AuthIBMDB2GroupCaching is "On,"
group information is cached in a file. |
| AuthIBMDB2CacheFile | /tmp/auth_cred_cache | Defines the location and name of
the cache file. |
| AuthIBMDB2CacheLifetime | 300 | Sets the expiration timeout in
seconds of the cached elements. |
In this example, we will use /var/www as the Web root and /data/private for the /private alias. To connect to the database, enter the user name tessarek and the password helmut. The name of the database is auth.
If you need to protect the Web location http://yourserver/private using the new installed mod_auth(nz)_ibmdb2 module:
First, you need to create the two tables in DB2 UDB that will be used for storing the users and the groups.
db2 => connect to auth user tessarek using helmut Database Connection Information Database server = DB2/LINUX 9.5.0 SQL authorization ID = TESSAREK Local database alias = AUTH db2 => CREATE TABLE USERS ( USERNAME VARCHAR(40) NOT NULL, PASSWORD VARCHAR(40) ) db2 => ALTER TABLE USERS ADD PRIMARY KEY (USERNAME) db2 => CREATE TABLE GROUPS ( USERNAME VARCHAR(40) NOT NULL, GROUPNAME VARCHAR(40) NOT NULL ) db2 => ALTER TABLE GROUPS ADD PRIMARY KEY (USERNAME,GROUPNAME) |
The tables do not need to be created as in the example above. They could be views or they could have additional columns. The point that has to be taken into account is that there must be a username and a password column in the usertable and the username column must be unique. Furthermore, there must be a username and a groupname column in the grouptable and the username and groupname columns must be unique. You can choose whatever names you want for the username, groupname, and password columns.
Next, you need to insert user and group information in the newly created tables. If you don't want to do any of the following:
- Use clear text passwords
- Use Apache's htpasswd to create a new encrypted password
- Copy the encrypted password from an existing userfile
You will have to install the UDFs as described in the next section.
db2 => insert into users (username,password) values ('test', apr_md5('testpwd'))
db2 => insert into groups (username,groupname) values ('test','admin')
db2 => select * from users
USERNAME PASSWORD
---------------------------------------- ----------------------------------------
test $apr1$bZGrA...$Ic7E68bhwdDnpX6XG/w8i/
1 record(s) selected.
db2 => select * from groups
USERNAME GROUPNAME
---------------------------------------- ----------------------------------------
test admin
1 record(s) selected.
|
Note: apr_md5 is a User Defined Function that is explained in the UDFs section of this article.
Finally, you need to edit the httpd.conf file and add the following lines. For the changes to be effective, the Web server has to be restarted.
Alias /private "/data/private/"
<Directory "/data/private">
AuthName "DB2 Authentication for private"
AuthType Basic
AuthBasicProvider ibmdb2 # Apache >= 2.2.x only
AuthIBMDB2User tessarek
AuthIBMDB2Password helmut
AuthIBMDB2Database auth
AuthIBMDB2UserTable users
AuthIBMDB2NameField username
AuthIBMDB2PasswordField password
AuthIBMDB2CryptedPasswords On
AuthIBMDB2KeepAlive On
AuthIBMDB2Authoritative On
AuthIBMDB2NoPasswd Off
AuthIBMDB2GroupTable groups
AuthIBMDB2GroupField groupname
require group admin
AllowOverride None
</Directory>
|
You also could have omitted the parameters AuthIBMDB2NameField, AuthIBMDB2PasswordField, AuthIBMDB2CryptedPasswords, AuthIBMDB2KeepAlive, AuthIBMDB2Authoritative, AuthIBMDB2NoPasswd because we are using the default values.
If the tables are declared without a schema name, the username that is used for connecting to the database is taken for the schema name.
Point your Web browser to http://yourserver/private and see what happens. If everything works, you should see something like in Figure 1.
Figure 1. Protected area
For sites with a large number of requests it makes sense to cache the user credentials so that the database requests are minimized. The caching is turned off by default so you have to enable caching with the directives AuthIBMDB2Caching On and AuthIBMDB2GroupCaching On. The cache file is created in the /tmp filesystem, since usually everybody has write access to that filesystem. You can change the path to the cache file with AuthIBMDB2CacheFile. The lifetime of the user credentials within the cache file is set with AuthIBMDB2CacheLifetime. By default the lifetime is set to 300 seconds (5 minutes).
If you want to use caching in RAM, create a RAM disk and point AuthIBMDB2CacheFile to that new filesystem. Make sure that the user, which is used to run Apache, has read and write access to that filesystem.
You can create a RAM disk in Linux with the following command (this RAM disk will be 5 MB in size):
mount -t tmpfs -o size=5M,nr_inodes=2k,mode=770 tmpfs /mnt/ramfs |
The module supports stored procedures to retrieve the user and group information from the database. The reasons for using stored procedures are ease of administration and performance. They must follow a certain parameter format.
The stored procedure for user authentication expects one input parameter, the username, and one output value, the password for that user:
PROCEDURE user_procedure_name ( IN VARCHAR, OUT VARCHAR )
The stored procedure for group authentication expects one input parameter, the user, and must return an open cursor to the resultset:
PROCEDURE group_procedure_name ( IN VARCHAR )
Here is an example of two stored procedures which are modelled on the table configuration in the practical sample.
File 1: sp.sql
CREATE PROCEDURE user_sp (IN v_username VARCHAR(40), OUT v_password VARCHAR(40)) LANGUAGE SQL BEGIN SELECT password INTO v_password FROM users WHERE username = v_username; END@ CREATE PROCEDURE group_sp (IN v_username VARCHAR(40)) LANGUAGE SQL DYNAMIC RESULT SETS 1 BEGIN DECLARE res CURSOR WITH RETURN FOR SELECT groupname FROM groups WHERE username = v_username; OPEN res; END@ |
You can install the stored procedures by running the following commands as the instance user:
[db2inst1@atvie01s ~]$ db2 connect to auth user tessarek using helmut [db2inst1@atvie01s ~]$ db2 -td@ -f sp.sql |
If you want to use the stored procedures, you have to edit the httpd.conf file and to restart the Web server again.
Alias /private "/data/private/"
<Directory "/data/private">
AuthName "DB2 Authentication for private"
AuthType Basic
AuthBasicProvider ibmdb2 # Apache >= 2.2.x only
AuthIBMDB2User tessarek
AuthIBMDB2Password helmut
AuthIBMDB2Database auth
AuthIBMDB2UserProc user_sp
AuthIBMDB2GroupProc group_sp
require group admin
AllowOverride None
</Directory>
|
Open a terminal and log in as DB2 UDB instance user. Then change to a directory where you are going to extract the db2-auth-udfs-1.3.tar.gz. (in this example I will use the directory /ext).
[db2inst1@atvie01s ~]$ cd /ext [db2inst1@atvie01s ext]$ tar -xzf db2-auth-udfs-1.3.tar.gz [db2inst1@atvie01s ext]$ db2-auth-udfs [db2inst1@atvie01s db2-auth-udfs]$ ls -l total 56 -rw-rw-r-- 1 db2inst1 db2grp1 596 2008-01-04 09:03 CHANGES -rw-rw-r-- 1 db2inst1 db2grp1 102 2006-02-17 11:03 drop_udfs.ddl -rw-rw-r-- 1 db2inst1 db2grp1 4407 2008-01-04 08:58 hash.c -rw-rw-r-- 1 db2inst1 db2grp1 55 2007-03-06 03:52 hash.def -rw-rw-r-- 1 db2inst1 db2grp1 3550 2007-02-27 03:36 hash.h -rw-rw-r-- 1 db2inst1 db2grp1 892 2007-03-06 03:52 INSTALL -rwxrwxr-x 1 db2inst1 db2grp1 740 2007-03-06 03:52 maketest -rw-rw-r-- 1 db2inst1 db2grp1 377 2007-03-06 03:45 maketest.bat -rwxrwxr-x 1 db2inst1 db2grp1 1218 2007-03-27 18:27 makeudf -rw-rw-r-- 1 db2inst1 db2grp1 532 2007-03-06 03:45 makeudf.bat -rw-rw-r-- 1 db2inst1 db2grp1 1131 2007-03-06 03:52 README -rw-rw-r-- 1 db2inst1 db2grp1 1020 2008-01-04 08:57 reg_udfs.ddl -rw-rw-r-- 1 db2inst1 db2grp1 595 2007-02-27 03:40 test_hash.c |
You will find a makeudf script within this directory that we will use to build the UDFs. For this you have to change a parameter in the script. Use an editor for changing the following parameter to the appropriate value for your environment.
DB2PATH=/home/db2inst1/sqllib
Set DB2PATH to the directory where DB2 is accessed. This is usually the instance home directory.
After changing the above parameter, run the script:
[db2inst1@atvie01s db2-auth-udfs]$ ./makeudf |
To register the UDFs, connect to your database and run the script to register the UDFs:
[db2inst1@atvie01s db2-auth-udfs]$ db2 connect to auth user tessarek using helmut
Database Connection Information
Database server = DB2/LINUX 9.5.0
SQL authorization ID = TESSAREK
Local database alias = AUTH
[db2inst1@atvie01s db2-auth-udfs]$ db2 -tvf reg_udfs.ddl
CREATE FUNCTION md5(VARCHAR(120))
RETURNS VARCHAR(32)
NOT FENCED
DETERMINISTIC
NO SQL
NO EXTERNAL ACTION
LANGUAGE C
RETURNS NULL ON NULL INPUT
PARAMETER STYLE SQL
EXTERNAL NAME 'hash!md5'
DB20000I The SQL command completed successfully.
CREATE FUNCTION apr_md5(VARCHAR(120))
RETURNS VARCHAR(37)
NOT FENCED
NO SQL
NO EXTERNAL ACTION
LANGUAGE C
RETURNS NULL ON NULL INPUT
PARAMETER STYLE SQL
EXTERNAL NAME 'hash!aprmd5'
DB20000I The SQL command completed successfully.
CREATE FUNCTION apr_crypt(VARCHAR(120))
RETURNS VARCHAR(13)
NOT FENCED
NO SQL
NO EXTERNAL ACTION
LANGUAGE C
RETURNS NULL ON NULL INPUT
PARAMETER STYLE SQL
EXTERNAL NAME 'hash!aprcrypt'
DB20000I The SQL command completed successfully.
CREATE FUNCTION apr_sha1(VARCHAR(120))
RETURNS VARCHAR(33)
NOT FENCED
DETERMINISTIC
NO SQL
NO EXTERNAL ACTION
LANGUAGE C
RETURNS NULL ON NULL INPUT
PARAMETER STYLE SQL
EXTERNAL NAME 'hash!aprsha1'
DB20000I The SQL command completed successfully.
[db2inst1@atvie01s db2-auth-udfs]$ db2 connect reset
|
The UDFs are written in ANSI C and are using the APR libraries. They should compile and run on all platforms. You can either use the bldrtn script in your sqllib/samples/c directory as a good start, or you can modify the makeudf script.
Here is a list of the UDFs, an example output, and a description.
|
UDF name
|
Example Output
|
Description
|
|---|---|---|
|
|
|
|
| md5 | 342df5b036b2f28184536820af6d1caf | MD5 hash. The MD5 function is compatible to the PHP MD5 function. |
| apr_md5 | $apr1$bhX6Z/..$kAxtFQozYsZhgNgqVnJd0. | Seeded MD5 hash. The apr_md5 is compatible to the Apache function that is used in the htpasswd utility. |
| apr_crypt | bhqWHYBAJAQ2w | UNIX® crypt. The apr_crypt is compatible to the Apache function that is used in the htpasswd utility. |
| apr_sha1 | {SHA}mO8HWOaqxvmp4Rl1SMgZC3LJWB0= | SHA1 algorithm. The apr_sha1 is compatible to the Apache function that is used in the htpasswd utility. |
If you set LogLevel debug in the httpd.conf file, you will get detailed information about what is happening during the authentication process. Here are two samples of the debugging text that was generated during the practical sample.
Sample 1. Debugging the authentication process with caching turned off
[notice] Apache/2.2.6 (Unix) mod_ssl/2.2.6 PHP/5.2.5 mod_authnz_ibmdb2/1.26 configured --
resuming normal operations
[info] Server built: Sep 8 2007 11:57:34
[debug] prefork.c(991): AcceptMutex: sysvsem (default: sysvsem)
[debug] mod_authnz_ibmdb2.c(1134): begin authenticate for user=[test], uri=[/private/]
[debug] mod_authnz_ibmdb2.c(523): begin get_ibmdb2_pw()
[debug] mod_authnz_ibmdb2.c(232): DB connection is dead or nonexistent;
create connection
[debug] mod_authnz_ibmdb2.c(235): allocate an environment handle
[debug] mod_authnz_ibmdb2.c(690): query=[SELECT rtrim(password) FROM users
WHERE username='test']
[debug] mod_authnz_ibmdb2.c(692): allocate a statement handle
[debug] mod_authnz_ibmdb2.c(698): prepare the statement
[debug] mod_authnz_ibmdb2.c(715): execute the statement
[debug] mod_authnz_ibmdb2.c(772): fetch each row, and display
[debug] mod_authnz_ibmdb2.c(788): get data from query resultset
[debug] mod_authnz_ibmdb2.c(795): password from database=
[$apr1$bZGrA...$Ic7E68bhwdDnpX6XG/w8i/]
[debug] mod_authnz_ibmdb2.c(797): call SQLFetch() (point to next row)
[debug] mod_authnz_ibmdb2.c(803): free statement handle
[debug] mod_authnz_ibmdb2.c(309): keepalive on; do not disconnect from database
[debug] mod_authnz_ibmdb2.c(813): end get_ibmdb2_pw()
[debug] mod_authnz_ibmdb2.c(877): begin get_ibmdb2_groups()
[debug] mod_authnz_ibmdb2.c(227): DB connection is alive; re-using
[debug] mod_authnz_ibmdb2.c(913): query=[SELECT rtrim(groupname) FROM groups
WHERE username='test']
[debug] mod_authnz_ibmdb2.c(915): allocate a statement handle
[debug] mod_authnz_ibmdb2.c(921): prepare the statement
[debug] mod_authnz_ibmdb2.c(938): execute the statement
[debug] mod_authnz_ibmdb2.c(1010): fetch each row, and display
[debug] mod_authnz_ibmdb2.c(1035): get data from query resultset
[debug] mod_authnz_ibmdb2.c(1050): group #1 from database=[admin]
[debug] mod_authnz_ibmdb2.c(1052): call SQLFetch() (point to next row)
[debug] mod_authnz_ibmdb2.c(1058): free statement handle
[debug] mod_authnz_ibmdb2.c(309): keepalive on; do not disconnect from database
[debug] mod_authnz_ibmdb2.c(1068): end get_ibmdb2_groups()
|
Sample 2. Debugging the authentication process with caching turned on
[notice] Apache/2.2.6 (Unix) mod_ssl/2.2.6 PHP/5.2.5 mod_authnz_ibmdb2/1.26 configured --
resuming normal operations
[info] Server built: Sep 8 2007 11:57:34
[debug] prefork.c(991): AcceptMutex: sysvsem (default: sysvsem)
[debug] mod_authnz_ibmdb2.c(1134): begin authenticate for user=[test], uri=[/private/]
[debug] caching.h(211): user [test] - [$apr1$bZGrA...$Ic7E68bhwdDnpX6XG/w8i/]
found in cache
[debug] caching.h(502): groups for user [test] found in cache
|
Although there are scripts for building the modules and the UDFs on Windows, most Windows users don't have a development environment installed to compile the packages.
You can download the latest binaries for Windows 32bit in the binary releases package section on the website of mod_auth(nz)_ibmdb2.
Learn
- "Speed-start your
Linux app 2003: Installing DB2 Version 8.1" (developerWorks, March 2004): Install and configure DB2 Version 8.1 for Linux and the Java Runtime Environment.
-
Official Web site of
mod_auth(nz)_ibmdb2: Find sources, latest news, FAQs, and more.
-
developerWorks Information Management zone: Learn more about DB2. Find technical documentation, how-to articles, education, downloads, product information, and more.
-
Stay current with developerWorks
technical events and webcasts.
Get products and technologies
- Download the source code for mod_authnz_ibmdb2 used in this article.
- Download the source code for mod_auth_ibmdb2.
- Download the source code for the UDFs used in this article.
- Download the documentation as a PDF file.
- Download the binaries for Windows 32bit.
-
Build your next development project with
IBM
trial software, available for download directly from developerWorks.
Discuss
-
Participate in developerWorks blogs and get involved in the developerWorks community.

Helmut K. C. Tessarek works at the IBM Toronto Lab in the area of DB2 performance and development. Before that he worked as an IBM DB2 specialist in Software Group Services at IBM Austria. He is the author of mod_auth(nz)_ibmdb2. You can reach Helmut at tessarek@ca.ibm.com.
Comments (Undergoing maintenance)





