Powered by Blogger.

Running CGI's from User Home Directories

You're trying to get CGI scripting to work on Apache from a user's home directory, and you're having problems. You may even be plagued by this error:
    [error] (13)Permission denied: exec of /home/user/public_html/junk.pl failed
    [error] [client xxx.xxx.xxx.xxx] Premature end of script headers: /home/user/public_html/junk.pl
This article can help you past this problem, though it does assume you are at least familiar with basic Apache configuration concepts.

First the Obvious

Here are some common errors, all very important to check out.
  1. Is Apache even running, use apachectl to be sure.
  2. Does the script contain any illegal control characters?
    This can easily happen if you edit on Windows and move the file over to Unix.
  3. Is the program specified by #! existing?
    use the where command to be sure.
  4. Are you emitting a Content-type line with a blank line separating from your page's content?
  5. Does the web server account have permission to read the file?
    Use su to become the web account and try it out.
  6. Does the file have execute permissions on it? It should.
    use the chmod command to remedy.

A Sample CGI Script

Here's a CGI script that works. Put it in your public_html subdirectory.
    junk.pl
    #!/usr/bin/perl
    
    print "Content-type: text/html\n\n";
    print "<html><body><h1>Hello World</h1></body></html>\n";
    
Enter $ where perl and edit the above script to use that path instead.

Making Sure httpd.conf is Right

Apache's httpd.conf file contains a section that looks like this:
    <Directory /home/*/public_html>
       AllowOverride FileInfo AuthConfig Limit
       Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
       <Limit GET POST OPTIONS PROPFIND>
          Order allow,deny
          Allow from all
       </Limit>
       <LimitExcept GET POST OPTIONS PROPFIND>
          Order deny,allow
          Deny from all
       </LimitExcept>
    </Directory>
That Options line must also contain the word ExecCGI. If you are using the All directive, this does not implictly cover ExecCGI as well, you must add it.
For example, the line above would become:
  Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec ExecCGI
This simply lets Apache know that anything in the /home/*/public_html directory is allowed to run CGI programs.
Next, we have to tell Apache what handler to use for various cgi extensions. Find a line that looks like this:  AddHandler cgi-script .cgi
This line says, "if you see a .cgi program, then invoke the cgi-script handler." We need to add other extensions. Modify the line to read:   AddHandler cgi-script .cgi .pl
You can also append other extensions like .sh.py, and so forth.
If you want to have default index.html-like files that are really CGI, then you'll also need to modify another directive. Look for this:
  DirectoryIndex index.html
This lists, in order from left to right, the default filenames to try if none is specified. Change it to something like this:   DirectoryIndex index.cgi index.html
You may also want to add index.plindex.pyindex.sh, etc. PHP users may want index.php, and Microsoft users may want index.htm which is different from index.html on Unix.

500 Internal Server Error


If you are browsing to your home page, http://www.yourdomain.com/~user/junk.pl, and you see something like the following, your problem may be suexec:

    Internal Server Error

    The server encountered an internal error or misconfiguration and was unable to complete your request.Please contact the server administrator, wls@wwco.com and inform them of the time the error occurred, and anything you might have done that may have caused the error.
    More information about this error may be available in the server error log.


    Apache/1.3.26 Server at www.yourdomain.com Port 80
If you find that your CGI's work when you're not using suexec, but don't when you are, then keep reading. It's impotant you know why.

How to tell if you are using suexec

Go to your /usr/local/apache/logs directory, find error_log, and look inside and see if this string appears:
[notice] suEXEC mechanism enabled (wrapper: /usr/local/apache/bin/suexec)
It will, whenever Apache is started.
In the above example, the suexec program is located at /usr/local/apache/bin/suexec, your location may vary.
Where should your copy be? Execute this command to find out (the part you're looking for is the response highlighted below):
    cd /usr/local/apache/bin
    ./httpd -V | grep SUEXEC_BIN
     -D SUEXEC_BIN="/usr/local/apache/bin/suexec"

A quick word about suexec

If you use the httpd.conf directives above to enable CGI mode, there are two ways the server can invoke user based-CGI programs:
  1. Without suexec
    The CGI program will run as the web server user. This means a user can construct scripts and have access to anything the web server does.
  2. With suexec
    The CGI program will run as the user who's account it's in. The catch here is that there are a pile of pre-conditions that absolutely must take place, or suexec won't let you run the CGI.The correct permissions are:
    chown root suexec
    chgrp root suexec
    chmod 4711 suexec
If you're willing to go with option #1, simply rename the suexec program to something else and restart the webserver. User based CGI problems will run now, but they'll run as the web server:
    cd /usr/local/apache/bin
    mv suexec suexec.disabled
    ./apachectl stop
    ./apachectl start
The advantages to using method #2 are two fold. First of all, the user can't access files accessable by the web server (such as .php files with database passwords in them), and secondly the user's scripts are capable of accessing files the web server might not normally have permission to access. Note, however, there are a pile of preconditions that must be met before this will work. Violate any one of them and you'll get a 550 error.
The Apache developers have listed all of these preconditions and installation steps in the document:
http://httpd.apache.org/docs/suexec.html

Bonehead Problem That Plauged Me

Here's a list of the problems that were preventing me from running suexec. Be sure you've addressed everything the developers listed.
  1. suexec must exist.
    I had renamed my copy to suexec.disabled, which forced the system to "work," though not securely. When I upgraded Apache, that installation saw there was no suexec in the expected place and put it back. This created the illusion that Apache had broken my CGI. Is hadn't, it had made it more secure as it should have been to start with.
  2. suexec must be runable by the web server's account.
    I had the right file permissions, but I had locked down the /usr/local/apache/bin directory so that only root could run programs there. This is why Apache may make a /usr/local/apache/sbin directory forsuexec. Simply su to the web server's account as root, cd to the directory with suexec in it, and try to run it.
  3. I didn't configure suexec when I compiled it.
    I thought the out of the box configuration would work. For advanced configurations, it may not.suexec must be run by the web server account. How does it know which one that is? You edit a #define in its suexec.h file before compiling. It default to www, but I was using web.
    Note, suexec can not run as root, which means your web server can not run as root. (Nor should it, especially if you are security concious.)
    There are a lot of other options, such as the mortal user account's UID number must be over a specified value (so people can't hack a system account). Most of your accounts in /etc/passwd will have low numbers for system tasks, and much larger numbers for real people. Same for GIDs.
  4. Apache will only look in one place for suexec.
    Don't think you can move it. Use the # ./httpd -V | grep SUEXEC_BIN trick to see where it's got to go. Advanced users can twiddle with Apache's configuration options to change the locations.
  5. suexec needs a place to log.
    Again, I had locked down the log directory so that only the web server could log there. This preventedsuexec, which was running as setuid root from writing a log file. My solution was to make Apache's log directory owned by the web server and the group be root, owner and group were given write access.Once I did this, suexec was writing a more descript file in cgi.log about failures.
  6. The UID/GID of the user must match that of public_html, which must also match that of the CGI program.
    A while back I set things up so that my public_html was owned by me, but the web server had group access. The reason I did this was because I wasn't running my web server as group users, because I didn't want users pawing through my raw web files.The cgi.log file was more than happy to point this out:
    info: (target/actual) uid: (wls/wls) gid: (users/users) cmd: junk.pl
    error: target uid/gid (503/100) mismatch with directory (503/300) or program (503/100)

    Note that all three of these values didn't match. Fix 'em, and you're set.

Multiple Groups

This begs the question then, if Apache doesn't handle group permissions correctly, how does one fix it?
The problem is that if you physically login to the web server account, you get the UID/GID of the web server andany additional groups listed in /etc/group. This is not the case when one uses the User and Group directives inhttpd.conf.
The solution is to compile Apache with the MULTIPLE_GROUPS preparser directive. While the capability exists, it certainly isn't clearly docmented how to do this in the Apache documentation. But enabling the feature does allow for all sorts of collaberation via groups that aren't normally possible.
To turn it on, do your ./configure, edit the file apache_x.y.z/src/apaci, and append this to the end of the file:
    echo '-DMULTIPLE_GROUPS'
Then compile apache as normal via make and make install.
I've written two news group threads on the subject of using MULTIPLE_GROUPS:
    Blogger Comment
    Facebook Comment