CGI Security Documentation

CGIWrap Installation Instructions

This page brings together the main CGIWrap installation guidance, core configuration options, password-protected deployment notes, and a real-world build example. It is intended for Unix administrators who need to compile, install, and secure CGIWrap correctly before making it available in a production CGI environment.

There are two sets of installation instructions here: the basic installation process used as a standard reference, and a contributed real-world example configuration from Piotr Klaban. Together they provide both a clean quick-start path and a more detailed example for administrators who want to compare against a working deployment.

Quick Installation

The quick installation path below covers the core sequence required to build CGIWrap, install it into the proper CGI directory, assign the right ownership and permissions, and create the expected companion names such as cgiwrapd and nph-cgiwrap.

Important: CGIWrap must be installed carefully. In particular, ownership, permissions, and the separation between CGIWrap and directly executable scripts matter a great deal. A careless setup can undermine the security checks CGIWrap is meant to provide.

1Check platform-specific notes first

Look at the notes to see if there are any hints for your platform before you begin compiling.

2Review available configure options

Issue ./configure --help to find the available configuration options. You will need to specify some of them. At the very least, you will usually want to specify --with-httpd-user=USERID.

3Run configure with the options you need

Type ./configure <options> using the options appropriate for your server, CGI paths, and local environment.

4Compile the program

Type make.

5Use make install if an install directory was specified

If you specified the installation directory in the options, you can type make install to do the remaining file placement and linking steps automatically.

6Copy CGIWrap into the server CGI directory

Copy the cgiwrap executable into your server's cgi-bin directory if you are not using make install.

7Set root ownership and setuid permissions

Make CGIWrap owned by root, executable by all, and setuid. This step must be performed while logged in as root.

chown root cgiwrap
chmod 4755 cgiwrap
8Create companion links in the CGI directory

Hardlink or symlink nph-cgiwrap, nph-cgiwrapd, and cgiwrapd to cgiwrap in the same directory.

ln [-s] cgiwrap cgiwrapd
ln [-s] cgiwrap nph-cgiwrap
ln [-s] cgiwrap nph-cgiwrapd
9Adjust permissions more tightly if needed

You can install CGIWrap with less permissive permissions such as 4750, but if you do, make sure the group of cgiwrap matches the group the server runs as.

10Avoid direct execution in the main cgi-bin directory

Do not allow non-trusted users to run scripts directly out of the main cgi-bin directory. If they can run scripts as the same userid as the web server, they may be able to subvert some of CGIWrap's security checks and use CGIWrap to run other users' scripts. Once CGIWrap is installed, it is strongly recommended that you not run any user scripts directly on the web server outside of the wrapper model.

Configuration Options

The following are options available with the configure command. The original notes indicate that items shown in boldface are highly recommended. Defaults can be seen by issuing ./configure --help or by looking at the config.h file after you have run configure.

At an absolute minimum, you will probably want to specify the --with-install-dir and --with-httpd-user options.

--with-perl=PATH
Path to Perl executable to use.
--with-local-contact-name=NAME
Specify the name of the local contact.
--with-local-contact-email
Specify the local contact's email address.
--with-local-contact-phone
Specify the local contact's phone number.
--with-local-contact-url
Specify a URL for the local contact.
--with-local-site-url
Specify a URL for this site.
--with-local-doc-url
Specify a URL for a local copy of the CGIWrap documentation.
--with-wall
Add the -Wall option for compilation with gcc, intended primarily for development debugging.
--with-install-group=GROUP
Group to install CGIWrap as.
--with-install-dir=PATH
Path to installation directory. This should be the path to your server's cgi-bin directory.
--with-cgi-dir=PATH
Path relative to home directory for CGI scripts.
--with-multiuser-cgi-dir=PATH
Define a central CGI script directory searched if the script is not found in a user directory. This can be dangerous if not designed carefully and is not needed for normal usage.
--with-httpd-user=USER
Define what userid the web server is running as. This is required.
--without-check-httpd-user
Do not check to make sure CGIWrap is being run by the server userid. This is not recommended.
--without-check-owner
Disable check for matching owner.
--without-check-group
Disable check for matching group.
--without-check-setuid
Disable check for setuid script.
--without-check-setgid
Disable check for setgid script.
--without-check-group-writable
Disable check for group writable script.
--without-check-world-writable
Disable check for world writable script.
--without-check-symlink
Disable check for symlinked script.
--with-check-shell
Enable check for a valid user shell.
--with-require-redirect-url
Require that REDIRECT_URL be set in the calling environment.
--with-chroot=PATH
Chroots script to PATH prior to execution and requires a specific environment. See the chroot docs for more details.
--with-minimum-uid=UID
Set the minimum uid of user that can use CGIWrap. Defaults to 100.
--with-minimum-gid=GID
Set the minimum gid or auxiliary gid of user that can use CGIWrap. Not enabled by default.
--with-logging-syslog=LABEL
Enable logging of script execution to syslog.
--with-logging-file=FILE
Enable logging of script execution to file.
--without-script-subdirs
Prevent users from storing scripts in subdirectories.
--without-redirect-stderr
Do not redirect stderr to stdout in scripts.
--without-initgroups
Disable use of initgroups() to clear non-userid auxiliary groups.
--without-setgroups
Disable use of setgroups() to add userid auxiliary groups.
--with-rewrite=FILE
Use a file to rewrite user directories.
--with-setenv-path=STRING
Set the PATH environment variable to STRING.
--with-setenv-tz=STRING
Set the TZ environment variable to STRING.
--with-rlimit-cpu=SECONDS
Limit CPU time with setrlimit.
--with-rlimit-vmem=BYTES
Limit total virtual memory with setrlimit.
--with-rlimit-as=BYTES
Limit total available memory with setrlimit.
--with-rlimit-fsize=BYTES
Limit writable file size with setrlimit.
--with-rlimit-data=BYTES
Limit data segment size with setrlimit.
--with-rlimit-stack=BYTES
Limit stack segment size with setrlimit.
--with-rlimit-core=BYTES
Limit core file size with setrlimit.
--with-rlimit-rss=BYTES
Limit resident set size with setrlimit.
--with-rlimit-nproc=COUNT
Limit number of processes with setrlimit.
--with-rlimit-nofile=COUNT
Limit number of open files with setrlimit.
--with-rlimit-memlock=BYTES
Limit lockable memory with setrlimit.
--with-allow-file=FILE
Limit CGIWrap usage.
--with-deny-file=FILE
Limit CGIWrap usage.
--with-vhost-allow-dir=DIR
Limit CGIWrap usage specific to each vhost. To restrict Unixtools, for example, you would create the access file DIR/unixtools.
--with-vhost-deny-dir=DIR
Limit CGIWrap usage specific to each vhost.
--with-host-checking
Allow specifying hosts in allow/deny files.
--with-afs=DIR
Enable AFS setpag() support.

Password Protected Installation

The following are special additional instructions for installing a copy of CGIWrap that allows users to create access-controlled scripts. For this to work, you need to have a single common password file that will be used by all authenticated scripts.

1Re-run configure with an authenticated CGI directory

Re-run configure and specify public_html/auth-cgi-bin instead of public_html/cgi-bin for the CGI directory.

2Compile again

Type make.

3Create and install into a new auth-cgi-bin directory

Create a new server CGI directory called auth-cgi-bin and install this new copy of CGIWrap into that directory the same way you installed it into the normal cgi-bin directory, including all expected linked names and permissions.

4Require authentication at the directory level

Place a .htaccess file or equivalent configuration in the auth-cgi-bin directory where CGIWrap is located so that authentication is required for access to files in that directory, while still allowing any valid user to get through.

5Validate execution identity and REMOTE_USER

Users can then verify that their script was actually executed by them, for example by checking real uid, and confirm that the REMOTE_USER environment variable contains a username they want to allow to access the script.

Supplementary Configuration

If you enabled access file checking, make sure you create the necessary files. If you enabled the user directory rewriting feature, make sure you create the configuration file specified in the configure run. These extra pieces are easy to overlook, but they matter because CGIWrap expects them to exist once those features are enabled.

Real-World Example Configuration

The following preserved example is included for administrators who want a more concrete reference. It outlines a practical deployment scenario, assumptions about Apache layout, PHP configuration, directory structure, build options, installation commands, and sample Apache configuration steps.

Here is the real-world example, for those who have problems
with cgiwrap installing.
Assumptions:
A. I use cgi-bin/ for global cgi scripts, you may choose /cgi-sys/ instead.
B. I have installed apache in /usr/local/apache/ directory, you may have
   it in different location (/etc/httpd/ etc.)
C. I use WWW as a user web directory ('UserDir WWW' Apache directive),
   you may choose public_html if you wish.
D. I allow users place .cgi and .php scripts everywhere under the WWW/ directory
   (--with-cgi-dir=WWW cgiwrap configuration option), you may choose another method.
E. I have system-wide html pages in /usr/local/apache/WWW/HTML
   ('DocumentRoot /usr/local/apache/WWW/HTML' apache directive)
   and system-wide cgi-bin/ directory in /usr/local/apache/WWW/cgi-bin/
   ('ScriptAlias' apache directive)

Make sure you have installed php, that you can run it from the command line,
and you see the '--enable-discard-path' configuration option in the output
of the following command:
  % php -i | grep configure

If not, then before/after installing cgiwrap, install php as a normal program
with the configuration option: --enable-discard-path

cgiwrap install procedure:
1. download and unpack cgiwrap archive
  % wget
  % gtar zxvf cgiwrap-3.8.tar.gz
  % cd cgiwrap-3.8

2. configure it:
Replace:
- /usr/local/bin/php with /usr/bin/php or other location of your php program
- WWW (in --with-cgi-dir=WWW) with public_html i.e. your users web directory
- /usr/local/apache/WWW/cgi-bin with /home/httpd/cgi-sys if you use such a value
  for ScriptAlias
- admin@man.torun.pl with YOUR contact address
- www (in --with-httpd-user=www) with apache if you run web server as 'apache' user

./configure --with-check-shell --with-rlimit-core=0 --with-rlimit-cpu=60 \
--without-redirect-stderr --without-logging-file --with-perl=/usr/bin/perl \
--with-httpd-user=www --with-cgi-dir=WWW \
--with-install-dir=/usr/local/apache/WWW/cgi-bin --with-wall \
--with-local-contact-email=admin@man.torun.pl --with-php=/usr/local/bin/php \
--with-php-interpreter

  # wait ...
  # wait more until configure checks everything

3. run make
  % make

That should output:
gcc -c -Wall -g -O2  -I. -I. debug.c
gcc -c -Wall -g -O2  -I. -I. util.c
util.c: In function `CheckUser':
util.c:370: warning: suggest parentheses around assignment used as truth value
util.c: In function `UserInFile':
util.c:1088: warning: subscript has type `char'
util.c:1096: warning: subscript has type `char'
gcc -c -Wall -g -O2  -I. -I. fetch.c
gcc -c -Wall -g -O2  -I. -I. stdutil.c
gcc -c -Wall -g -O2  -I. -I. msgs.c
gcc -o cgiwrap cgiwrap.o debug.o util.o fetch.o stdutil.o msgs.o

4. install with 'make install':
  % make install

That would be executed (make install -n):
rm -f /usr/local/apache/WWW/cgi-bin/cgiwrap
rm -f /usr/local/apache/WWW/cgi-bin/cgiwrapd
rm -f /usr/local/apache/WWW/cgi-bin/nph-cgiwrap
rm -f /usr/local/apache/WWW/cgi-bin/nph-cgiwrapd
# rm -f /usr/local/apache/WWW/cgi-bin/php-cgiwrap
# rm -f /usr/local/apache/WWW/cgi-bin/php-cgiwrapd
cp cgiwrap /usr/local/apache/WWW/cgi-bin/cgiwrap
chown root /usr/local/apache/WWW/cgi-bin/cgiwrap
chgrp root /usr/local/apache/WWW/cgi-bin/cgiwrap
chmod 4755 /usr/local/apache/WWW/cgi-bin/cgiwrap
ln /usr/local/apache/WWW/cgi-bin/cgiwrap /usr/local/apache/WWW/cgi-bin/cgiwrapd
ln /usr/local/apache/WWW/cgi-bin/cgiwrap /usr/local/apache/WWW/cgi-bin/nph-cgiwrap
ln /usr/local/apache/WWW/cgi-bin/cgiwrap /usr/local/apache/WWW/cgi-bin/nph-cgiwrapd
# ln /usr/local/apache/WWW/cgi-bin/cgiwrap /usr/local/apache/WWW/cgi-bin/php-cgiwrap
# ln /usr/local/apache/WWW/cgi-bin/cgiwrap /usr/local/apache/WWW/cgi-bin/php-cgiwrapd

Then you see you have cgiwrap in the /usr/local/apache/WWW/cgi-bin/
directory.

5. Now configure Apache:
  % cd /usr/local/apache/conf     (on linux: % cd /etc/httpd/conf/)
  % vim httpd.conf                (or % pico httpd.conf)

5a) add these lines to global directives:
# change /usr/local/apache/WWW/cgi-bin/ to YOUR /cgi-bin/ real path
ScriptAlias /cgi-bin/ "/usr/local/apache/WWW/cgi-bin/"
AddHandler cgi-wrapper .php
AddHandler cgi-wrapper .cgi
Action cgi-wrapper /cgi-bin/cgiwrap

5b) do not run cgiwrap on .cgi scripts in the global /cgi-bin/ directory:

AllowOverride None
Options None
AddHandler cgi-script .cgi


5c) I have a phpMyAdmin installed in /user/local/apache/WWW/HTML/phpMyAdmin/,
and I want it to be run as a 'www' user from the /WWW/HTML/ directory

Action cgi-wrapper /cgi-bin/cgiwrap/www/HTML


5d) I have some Virtual Domains defined. Every user can have its own
    virtual domain. I want .cgi and .php scripts to be run as a specified user
    ('makler' in this situation):

ServerAdmin
DocumentRoot /home/[... home directory/WWW here ...]/makler/WWW
ServerName www.klaban.torun.pl
ErrorLog logs/klaban_error_log
CustomLog logs/klaban_access_log combined
Action cgi-wrapper /cgi-bin/cgiwrap/makler
# [... other configuration stuff discarded ...]


6. Test your configuration before Apache reload:
  % ~www/bin/apachectl configtest
Syntax OK

7. Reload apache web server:
  % ~www/bin/apachectl stop
  % ~www/bin/apachectl start

8. Test if the simple php script is executed OK:

8a) create php script - your php scripts should be owned by the user, not root:
  % su makler
  % echo '