CGI and Perl

Directory Access Configuration

If you decide to allow per-directory configuration files, you'll need to enable them in the global access.conf file with a good deal of consideration and care. Assuming you have a <Directory> entry for the DocumentRoot, you can enable certain capabilities for individual directories beneath the DocumentRoot on an as-needed basis.

Let's suppose, for the purpose of demonstration, that your archive consists of the DocumentRoot:

/usr/local/etc/httpd/htdocs

And beneath DocumentRoot, you have a subdirectory:

/usr/local/etc/httpd/htdocs/test1

Now assume that there exists, for DocumentRoot, an entry in the access.conf file that looks something like this:

<Directory /usr/local/etc/httpd/htdocs>
 AuthType Basic
 AuthName PasswordAdmin
 AuthUserFile /usr/local/etc/httpd/conf/.htpasswd
 AccessFileName .myhtaccess
 AddType text/html .shtml
 Options Indexes SymLinksIfOwnerMatch IncludesNOEXEC
 AllowOverride AuthConfig FileInfo Indexes Limit
 </Directory>

This entry allows any directory beneath DocumentRoot to have its own .privaccess files, which can then override any of the directives specified for DocumentRoot except the Options directive, which we don't want to let anyone specify beyond what is already allowed. Note that the Options directive is already used to turn on the automatic directory indexes (when index.html doesn't exist) and to follow symlinks if the owner of the symlink and what it points to match, as well as allowing server-side includes, but not the #exec and #include directives within server-side includes files.

Now, assume that the directory test1 has some proprietary or confidential information within it. You could assure that nothing is left to chance by configuring its directory entry in the global access.conf to disallow any overrides or options, like this:

<Directory /usr/local/etc/httpd/htdocs/test1>
 AllowOverride None
 Options None
 <Limit GET POST>
 require valid-user
 </Limit>
 </Directory>

Note that we also specify here that any access to this directory also be validated from the valid users in the AuthUserFile, inherited from the directives for DocumentRoot. We'll look more at user/group authentication in a bit.

You can use Perl and the HTTPD modules to verify that your access configurations for any given file in the archive are what you expect them to be. Here we'll modify our previous script to give us some additional information on the configuration directives for each directory specified in access.conf:

use HTTPD::Config;
 require "stat.pl";
 $conf =  `/usr/local/etc/httpd/conf';
 @files = qw(httpd.conf srm.conf access.conf);
 $V= new HTTPD::Config (SERVER => Apache,
                              SERVER_ROOT => $conf,
                              FILES => [@files]);
 print "Userid: ", $V->user,"\n";
 print "Group: ", $V->group,"\n";
 print "Administrator is: ", $V->server_admin,"\n";
 print "Running at port: ", $V->port,"\n";
 print "Access filename: ",$V->access_file_name,"\n";
 print "User Directory: ", $V->user_dir,"\n";
 print "Global Types:\t", join("\n\t\t",$V->add_type),"\n";
 print "\n\n";
 foreach $dir (keys %{$V->{`Directory'}}){
     print "Options for Directory: $dir\n";
     while(($opt,$val) = each %{$V->{`Directory'}{$dir}{`OPTIONS'}}){
         print "\t",$opt, " : ", @{$val},"\n";
     }
     print "\tLimit: @{$V->{Directory}{$dir}{LIMIT}{METHODS}}\n";
     while(($key,$val) = each %{$V->{Directory}{$dir}{LIMIT}{OPTIONS}}) {
         print "\t\t$key = @{$val}\n";
     }
     print "\n";
 }
 # rudimentary permissions checking
 $webuser = (getpwnam($V->user))[2];
 opendir(ROOT,$V->server_root);
 @files = grep(!/\.\.?/,readdir(ROOT));
 closedir(ROOT);
 foreach $f (@files){
     @s = Stat($V->server_root."/$f");
     if($s[$ST_UID] == $webuser){
         print "Warning: ",$V->server_root,"/$f is owned by ",
                 $V->user,"\n\n";
     }
     if($f eq "httpd"){
         if(($s[$ST_MODE] != 0100700) or ($s[$ST_UID] != 0)){
             print "\tWarning: ",$V->server_root,"/httpd may have\n";
             print "\tpermission problems.  Recommend root ownership\n";
             print "\tand readable, writable and executable only by\n";
             print "\troot user\n";
         }
     }
 }

Note that we added a few lines to the foreach loop, which loops over the <Directory> entries in access.conf, to tell us about any <Limit> directives and how they're configured. Now when we run the script against our existing hierarchy, we get the following output:

Userid: nobody
 Group: nogroup
 Administrator is: wmiddlet@adobe.com
 Running at port: 80
 Access filename: .privaccess
 User Directory: public_html
 Global Types:   text/html .shtml
 Options for Directory: /usr/local/etc/httpd/cgi-bin
         Options : None
         AllowOverride : None
         Limit:
 Options for Directory: /usr/local/etc/httpd/htdocs/test1
         Options : None
         AllowOverride : None
         Limit: GET POST
                 require = valid-user
 Options for Directory: /usr/local/etc/httpd/htdocs
         PerlHandler : main::handler
         AddType : text/html .shtml
         Options : Indexes SymLinksIfOwnerMatch IncludesNOEXEC
         AllowOverride : AuthConfig FileInfo Indexes Limit
         Limit:

The <Limit> options for the test1 directory are now specified.