Only in plexus/src: CVS
diff -I^#! -I$Id: -N -u plexus/src/Makefile.run /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/Makefile.run
--- plexus/src/Makefile.run	Thu Aug 26 13:38:55 1993
+++ /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/Makefile.run	Thu Aug 26 11:17:29 1993
@@ -4,7 +4,8 @@
 UTIL = plexus.conf map.conf front.pl dir.pl site.pl \
     archieplex.pl bookmark.pl bsdi-man.pl calendar.pl date.pl decode.pl \
     finger.pl fortune.pl get.pl grep.pl mime.pl neuro.pl npindex.pl \
-    search.pl setext.pl siks.pl sunman.pl sunmanindex.pl
+    search.pl setext.pl siks.pl sunman.pl sunmanindex.pl \
+    hosts.conf access.pl
 
 install:
 	@-mkdir -p ${DESTDIR}${SDIR} 2>/dev/null || true
diff -I^#! -I$Id: -N -u plexus/src/access.pl /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/access.pl
--- plexus/src/access.pl
+++ /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/access.pl	Thu Aug 26 11:17:29 1993
@@ -0,0 +1,272 @@
+#!/usr/local/bin/perl
+# access.pl -- routines for controlling access to resources
+#
+# $Id: access.pl,v 1.4 1993/08/24 18:53:42 mcrae Exp $
+#
+# Chris Mcrae <mcrae@ckm.ucsf.edu>, August 1993
+#
+
+# read_hosts_conf_file()
+# - parse the host configuration file ($http_host_access_file) into
+# global arrays %method_allow and %method_deny.  The format of the
+# file is as follows.
+#
+# In the syntax below:
+#     'x'	denotes the character x, literally
+#     *		denotes "repeat the previous pattern 0 or more times"
+#     +		denotes "repeat the previous pattern 1 or more times"
+#     x|y       denotes "select one of the terms x or y"
+#     {xyz}	denotes "select one of x, y, or z"
+#     {x-y}	denotes "select one from the range x to y, inclusive"
+#     [item]	denotes item is optional
+#     
+# Format:
+#     method_list host_spec_list
+# where 
+#     method_list --> method[','method]
+#         method --> one of the methods defined via %method in
+#                    plexus.conf (get, textsearch, ...)
+#     host_spec_list --> host_spec[','host_spec_list]
+#         host_spec --> ['!']{host_name|host_address|partial_spec|ALL|NONE}
+#             host_name --> alpha[alphanum]*['.'host_ext]
+#                 host_ext --> alphanum+['.'host_ext]
+#             host_address --> {0-9}+['.'host_address]
+#             partial_spec --> '.'alphanum+['.'partial_spec]
+#                     alphanum --> {a-z}|{A-Z}|{0-9}|'-'
+#
+# ALL and NONE
+#   The special symbol "ALL" indicates that all hosts, by default,
+# should have access to the corresponding methods.  Special symbol
+# "NONE" # disallows access for all hosts not explicitly matched. 
+# These settings override the %DEFAULT_ACCESS array which is set in
+# plexus.conf and which otherwise has the same affect.  Note that
+# (!ALL == NONE) and (!NONE == ALL).
+#
+# Netgroups
+# Note: it would be nice to add netgroups to this spec, but first we gotta
+# get setnetgrent, getnetgrent, and endnetgrent into perl.  This could be
+# implemented via the "syscall" function perhaps.  Proposed syntax for
+# netgroups:
+#         host_spec --> host_name|host_address|partial_spec|netgroup
+#             netgroup --> '@'alphanum+
+#
+#
+sub read_hosts_conf_file {
+    local(@methods, @hosts, @allow, @deny);
+    local($negate, $method);
+    local(@addr, $addr);
+    local($all_or_none) = undef;
+    local($sig_tmp) = $SIG{'USR2'};
+
+    # not interested in these signals right now
+    $SIG{'USR2'} = 'IGNORE';
+
+    flock(LOG, &LOCK_EX);
+    print LOG "----Server #$$ reading host access file: $http_host_access_file at ", &ctime(time);
+    flock(LOG, &LOCK_UN);	# explicitly unlock the log
+
+    # clear globals
+    %method_allow = undef;
+    %method_deny = undef;
+
+    defined($http_host_access_file) || die "\$http_host_access_file not defined.\n";
+    open(CONF, $http_host_access_file) || die "failed to open $http_host_access_file\n";
+    while (<CONF>) {
+	chop;
+
+	# skip comments
+	next if /^\s*#/;
+
+	# extract the methods and the hosts
+	(split(/\s+/) != 2) &&
+	    die "malformed entry: $http_host_access_file: no spaces allowed within method_list or host_spec_list\n";
+	@methods = split(',', $_[0]);
+	@hosts = split(',', $_[1]);
+
+	# process each specified host
+	$all_or_none = undef;		# reset
+	@allow = (); @deny = ();	# zero these guys
+	foreach (@hosts) {
+	    # check for negation
+	    if (/^!/) {
+		# note the negation
+		$negate = 1;
+
+		# strip the bang off the host_spec
+		substr($_, 0, 1) = "";
+
+                # invert ALL and NONE
+		if (/^ALL$/) {
+		    $_ = "NONE";
+		    $negate = 0;
+		} elsif (/^NONE$/) {
+		    $_ = "ALL";
+		    $negate = 0;
+		}
+	    } else {
+		$negate = 0;
+	    }
+
+	    # trap special symbols "ALL", and "NONE",
+	    # pass partial names through,
+	    # otherwise try to resolve CNAMES to ip addresses
+	    if (/^ALL$/) {
+		(($all_or_none cmp "NONE") == 0) &&
+		    die "malformed entry: $http_host_access_file: special symbols ALL and NONE are mutually exclusive\n";
+		$all_or_none = "ALL";
+	    } elsif (/^NONE$/) {
+		(($all_or_none cmp "ALL") == 0) && 
+		    die "malformed entry: $http_host_access_file: special symbols ALL and NONE are mutually exclusive\n";
+		$all_or_none = "NONE";
+	    } elsif (!/^\.|\.$/ && /[a-zA-Z\-]+/) {
+		@addr = ();
+		foreach $addr ((gethostbyname($_))[4]) {
+		    $addr[$#addr+1] = &ip_addr($addr) if length($addr);
+		}
+		# OR multiple references together
+		if ($#addr >= $[) {
+		     $_ = join(':', ($_, @addr));
+		}
+	    }
+
+	    # build the list of hosts
+	    if ($negate) {
+		@deny[$#deny+1] = $_;
+	    } else {
+		@allow[$#allow+1] = $_;
+	    }
+	}
+
+	# for each specified method...add the corresponding
+	# list of hosts to the appropriate access control list
+	foreach $method (@methods) {
+	    defined($method{$method}) || die "unknown method: $method";
+	    if ($#allow >= $[) {
+		$method_allow{$method} && $method_allow{$method} .= ':';
+		$method_allow{$method} .= join(':', @allow);
+	    }
+	    if ($#deny >= $[) {
+		$method_deny{$method} && $method_deny{$method} .= ':';
+		$method_deny{$method} .= join(':', @deny);
+	    }
+	}
+    }
+    close(CONF);
+
+    # reset global flag
+    $reread_host_conf_file = 0;
+
+    # not interested in these signals right now
+    $SIG{'USR2'} = $sig_tmp;
+} # read_hosts_conf_file
+
+# reread_hosts_conf_file()
+#    - signal handler
+#    - set flag and return
+sub reread_hosts_conf_file {
+    $reread_host_conf_file = 1;
+} # reread_hosts_conf_file
+
+# verify_access()
+# verify the access priveleges of the current client with respect to
+# the given method.
+sub verify_access {
+    local($method) = @_;
+    local($addr, $name, @valid_addrs);
+
+    # so, who are we talking to?
+    $addr = (unpack($sockaddr, getpeername(NS)))[2];
+
+    # get the corresponding CNAME
+    $name = (gethostbyaddr($addr, &AF_INET))[0];
+
+    # try to catch cheaters
+    @valid_addrs = (gethostbyname($name))[4];
+    grep($_ eq $addr, @valid_addrs) || do {
+	printf LOG
+	    "hostname/address mismatch: %s != $name",
+		&ip_addr($addr);
+	&error(
+	    'unauthorized',
+	    "Failed to match your ip-address with your hostname.\nCheck your DNS (Domain Name Service) configuration"
+	)
+    };
+
+    # check the access control lists
+    return(&hostmatch($method, $name, &ip_addr($addr)));
+} # verify_access
+
+# find best match in the host access lists
+sub hostmatch {
+    local($method, $host, $addr) = @_;
+    local($default_access) = $DEFAULT_ACCESS{$method};
+    # $saw_none is set by bestmatch()
+    local($saw_none) = 0;
+    
+    # find the best match (if any) in the allow list
+    $best_allow = &bestmatch($host, $addr,
+				split(':', $method_allow{$method}));
+    # saw NONE in $method_allow{$method}, implies no access unless
+    # explicitly specified
+    (($best_allow < 0) && $saw_none) && do {
+	$saw_none = 0; $default_access = 0;
+    };
+
+    # find the best match (if any) in the deny list
+    $best_deny = &bestmatch($host, $addr,
+				split(':', $method_deny{$method}));
+    # saw NONE in $method_deny{$method}, access allowed unless
+    # explicitly denied
+    (($best_deny < 0) && $saw_none) && do {
+	$saw_none = 0; $default_access = 1;
+    };
+
+    # return the default if it's a toss-up
+    if ($best_allow == $best_deny) {
+	return($default_access);
+    }
+    # otherwise return the result
+    return($best_allow > $best_deny);
+} # hostmatch
+
+# return the length of the longest substring which matches any of the
+# strings. the special substring 'ALL' matches with length 0 only if
+# no other substrings do.
+sub bestmatch {
+    local($str0, $str1, @substrings) = @_;
+    local($best) = -1;
+    local($str, $substr);
+
+    foreach $str ($str0, $str1) {
+	foreach $substr (@substrings) {
+	    if (($substr cmp 'ALL') == 0) {
+		# ALL matches minimally
+		($best < 0) && do { $best = 0; };
+	    }
+	    elsif (($substr cmp 'NONE') == 0) {
+		# set flag defined by caller
+		$saw_none = 1;
+	    } elsif (index($str, $substr) > ($[-1)) {
+		# found a match, is it a better one?
+		if (length($substr) > $best) {
+		    $best = length($substr);
+		}
+	    }
+	}
+    }
+
+    return($best);
+} # bestmatch
+
+# print out an associative array (utility routine)
+sub print_assoc {
+	local(%a) = @_;
+
+	foreach(keys %a) {
+		printf("\t%s: %s", $_, $a{$_});
+		printf("\n");
+	}
+} # print_assoc
+
+1;
+
diff -I^#! -I$Id: -N -u plexus/src/front.pl /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/front.pl
--- plexus/src/front.pl	Thu Aug 26 13:38:53 1993
+++ /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/front.pl	Thu Aug 26 11:17:30 1993
@@ -1,8 +1,10 @@
 # front.pl -- process incoming requests
 #
-# $Id: front.pl,v 2.2 1993/07/08 14:21:27 sanders Exp $
+# $Id: front.pl,v 1.3 1993/08/24 18:56:54 mcrae Exp $
 #
 # by Tony Sanders <sanders@bsdi.com>, May 1993
+# Modified by Chris McRae <mcrae@ckm.ucsf.edu>, August 1993
+#    - added support for host-level access control, see access.pl
 #
 
 #
@@ -12,9 +14,17 @@
 sub process_input {
     local($method, $uri, $version, $waste) = split(" ", $_, 4);
     local(*in_headers, *out_headers);
+
     $method =~ y/A-Z/a-z/;			# XXX: $version =~ y/A-Z/a-z/;
-    &error('not_implemented', "Invalid Method: $method")
-        unless (defined $method{$method});
+    unless (defined $method{$method}) {
+	&log("Invalid Method");
+	&error('not_implemented', "Invalid Method: $method")
+    }
+    # check host access
+    unless (&verify_access($method)) {
+	&log("Access Denied");
+	&error('unauthorized', "Disallowed Method: $method");
+    }
     &parse_headers(*in_headers) if $version;	# XXX: =~ m/$htrq_version/i;
     # Convert escaped chars per URL spec (e.g., %20 is space)
     # XXX: $uri =~ s/%(\d+)/pack(c,hex($1))/eg;
diff -I^#! -I$Id: -N -u plexus/src/hosts.conf /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/hosts.conf
--- plexus/src/hosts.conf
+++ /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/hosts.conf	Thu Aug 26 11:17:30 1993
@@ -0,0 +1,48 @@
+#
+# hosts.conf -- default host access control file
+#
+# $Id: hosts.conf,v 1.3 1993/08/24 00:49:13 mcrae Exp mcrae $
+#
+# Christopher Mcrae <mcrae@ckm.ucsf.edu>, August 1993
+#
+# In the syntax below:
+#     'x'	denotes the character x, literally
+#     *		denotes "repeat the previous pattern 0 or more times"
+#     +		denotes "repeat the previous pattern 1 or more times"
+#     x|y       denotes "select one of the terms x or y"
+#     {xyz}	denotes "select one of x, y, or z"
+#     {x-y}	denotes "select one from the range x to y, inclusive"
+#     [item]	denotes item is optional
+#     
+# Format:
+#     method_list host_spec_list
+# where 
+#     method_list --> method[','method]
+#         method --> one of the methods defined via %method in
+#                    plexus.conf (get, textsearch, ...)
+#     host_spec_list --> host_spec[','host_spec_list]
+#         host_spec --> ['!']{host_name|host_address|partial_spec|ALL|NONE}
+#             host_name --> alpha[alphanum]*['.'host_ext]
+#                 host_ext --> alphanum+['.'host_ext]
+#             host_address --> {0-9}+['.'host_address]
+#             partial_spec --> '.'alphanum+['.'partial_spec]
+#                     alphanum --> {a-z}|{A-Z}|{0-9}|'-'
+#
+# ALL and NONE
+#   The special symbol "ALL" indicates that all hosts, by default,
+# should have access to the corresponding methods.  Special symbol
+# "NONE" # disallows access for all hosts not explicitly matched. 
+# These settings override the %DEFAULT_ACCESS array which is set in
+# plexus.conf and which otherwise has the same affect.  Note that
+# (!ALL == NONE) and (!NONE == ALL).
+#
+# Netgroups
+# Note: it would be nice to add netgroups to this spec, but first we gotta
+# get setnetgrent, getnetgrent, and endnetgrent into perl.  This could be
+# implemented via the "syscall" function perhaps.  Proposed syntax for
+# netgroups:
+#         host_spec --> host_name|host_address|partial_spec|netgroup
+#             netgroup --> '@'alphanum+
+#
+# allow all to read and search
+get	ALL
diff -I^#! -I$Id: -N -u plexus/src/plexus.conf /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/plexus.conf
--- plexus/src/plexus.conf	Thu Aug 26 13:38:52 1993
+++ /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/plexus.conf	Thu Aug 26 11:17:30 1993
@@ -3,6 +3,8 @@
 # $Id: plexus.conf,v 2.10 1993/07/16 18:50:21 sanders Exp $
 #
 # Tony Sanders <sanders@bsdi.com>, April 1993
+# Modified by Chris McRae <mcrae@ckm.ucsf.edu>, August 1993
+#    - added support for host-level access control, see access.pl
 #
 
 #
@@ -36,6 +38,23 @@
 $http_server            = "$http_sdir/plexus.pl";
 
 require "$http_sdir/site.pl";			# defines system params
+
+# host access configuration file
+$http_host_access_file  = $ENV{'HTTPD_ACCESS'} || "$http_sdir/hosts.conf";
+# configure default access priveleges.  these take affect
+# if the client system is not explicitly matched in the
+# host acess configuration file, or if the entries in that
+# file which do match are self-contradictory.
+%DEFAULT_ACCESS = (
+    'get',		1,
+    'textsearch',	1,
+    'head',		1,
+    'put',		0,
+    'post',		0,
+    'checkout',		0,
+    'checkin',		0,
+    'showmethod',	1
+);
 
 #
 # These define the mapping between HTTP Method and perl function.
diff -I^#! -I$Id: -N -u plexus/src/plexus.pl /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/plexus.pl
--- plexus/src/plexus.pl	Thu Aug 26 13:38:54 1993
+++ /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/plexus.pl	Thu Aug 26 11:17:30 1993
@@ -5,6 +5,8 @@
 #
 # Originally by Marc VanHeyningen, March 1993
 # Modified by Tony Sanders <sanders@bsdi.com>, April 1993
+# Modified by Chris Mcrae <mcrae@ckm.ucsf.edu>, August 1993
+#    - added support for host-level access control, see access.pl
 #
 # This code forms the core of a multi-threaded HTTP deamon, with the
 # primary emphasis being on responsiveness; thus, things like exec()
@@ -34,10 +36,15 @@
 select(LOG); $| = 1;
 print LOG "----Server #$$ on port $port started at ", &ctime(time);
 
+# read the hosts.conf file (sets %method_allow and %method_deny)
+require "$http_sdir/access.pl";
+&read_hosts_conf_file;
+
 $restart_daemon = 0;
 $SIG{'INT'} = "cleanup";
 $SIG{'QUIT'} = "restart_daemon";
 $SIG{'USR1'} = "restart_daemon";
+$SIG{'USR2'} = "reread_hosts_conf_file";
 $SIG{'CHLD'} = "reaper"; 
 
 open(S, "+>&$s") || die "reopen of socket filedescriptor failed: $!";
@@ -52,12 +59,11 @@
 	print LOG "----Server exiting: $@\n"; die "$@";
     }
 
-    # XXX: This would be the place to add code that filters connections
-    # XXX: Someone needs to make this configurable
-    # $mynet = pack("bbbb", 128, 46, 0 0);        # your Class B network
-    # $mymask = 0xffff0000;                       # for Class B
-    # $peer_net = ((unpack($sockaddr, getpeername(NS)))[2]) & $mymask;
-    # if ($peernet == $mynet && fork == 0) {...}
+    # unpack the addr
+    $peer = &ip_addr((unpack($sockaddr, $addr))[2]);
+
+    # make sure we have current information
+    &read_hosts_conf_file if $reread_host_conf_file;
 
     # fork immediately to prevent delays
     if (fork == 0) {
@@ -66,7 +72,7 @@
 	select(STDOUT); $| = 1;
 	$_ = <NS>;				# get request
 	s/[ \t\r\n]*$//;			# remove trailing white-space
-	&log;					# log request
+	&log($_);
 	&process_input;				# DO IT!
 	exit;
     }
@@ -76,6 +82,7 @@
 alarm($http_timeout);
 flock(LOG, &LOCK_EX) if $running_as_root;
 print LOG "----Server #$$ restarting at ", &ctime(time);
+flock(LOG, &LOCK_UN);	# explicitly unlock the log
 close(LOG);
 alarm(0);
 exec "$http_server", $http_config, $port, $running_as_root, $s;
@@ -86,14 +93,14 @@
 sub timeout_error { &error('internal_error',
     "Server timed out after $http_timeout seconds."); }
 sub cleanup { close(S); shutdown(S, 2); print STDOUT "\n\n"; exit; }
+# log connection
 sub log {
-    # log connection
-    local($af, $port, $inetaddr) = unpack($sockaddr, $addr);
-    local(@inetaddr) = unpack('C4', $inetaddr);
+    local($msg) = @_;
     local($ctime) = &ctime(time); chop $ctime;
+
     flock(LOG, &LOCK_EX) if $running_as_root;	# client logs so must lock
-    printf(LOG "%-15s %s %s\n", join(".", @inetaddr), $ctime, $_);
-    close(LOG);					# lock goes away
+    printf(LOG "%-15s %s %s\n", $peer, $ctime, $msg);
+    flock(LOG, &LOCK_UN);	# explicitly unlock the log
 }
 sub error {
     local($status, $msg) = @_;
diff -I^#! -I$Id: -N -u plexus/src/site.pl /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/site.pl
--- plexus/src/site.pl	Thu Aug 26 13:38:52 1993
+++ /local/mcrae/src/issg/plexus-2.2.1-with-access-control/src/site.pl	Thu Aug 26 11:17:30 1993
@@ -1,9 +1,11 @@
 #
 # site.pl -- Site dependent code (mostly defining system params)
 #
-# $Id: site.pl,v 2.1 1993/07/08 22:48:08 sanders Exp $
+# $Id: site.pl,v 1.2 1993/08/24 00:49:13 mcrae Exp $
 #
 # by Tony Sanders <sanders@bsdi.com>, Junly 1993
+# Modified by Chris McRae <mcrae@ckm.ucsf.edu>, August 1993
+#     - added &LOCK_UN and &ip_addr
 #
 
 #
@@ -16,6 +18,10 @@
 eval 'sub SOL_SOCKET {0xffff;}'	unless defined &SOL_SOCKET;
 eval 'sub SO_REUSEADDR {0x04;}' unless defined &SO_REUSEADDR;
 $sockaddr = 'S n a4 x8';	# socket structure format
+# unpack an addr
+sub ip_addr {
+    return(join('.', unpack("C4", $_[0])));
+}
 
 #
 # File locking
@@ -23,6 +29,7 @@
 do 'sys/file.ph';
 eval 'sub LOCK_SH {0x01;}'	unless defined &LOCK_SH;
 eval 'sub LOCK_EX {0x02;}'	unless defined &LOCK_EX;
+eval 'sub LOCK_UN {0x08;}'	unless defined &LOCK_UN;
 
 #
 # File/Directory support
