$NetBSD: patch-ad,v 1.2 2000/01/15 22:55:47 hubertf Exp $

diff -x *.orig -urN ./WWW/Library/Implementation/HTAAServ.c /usr/pkgsrc/www/lynx/work.unpatched/lynx2-8-2/WWW/Library/Implementation/HTAAServ.c
--- ./WWW/Library/Implementation/HTAAServ.c	Thu Jan  1 01:00:00 1970
+++ /usr/pkgsrc/www/lynx/work.unpatched/lynx2-8-2/WWW/Library/Implementation/HTAAServ.c	Sat Jan 15 07:57:17 2000
@@ -0,0 +1,638 @@
+
+/* MODULE							HTAAServ.c
+**		SERVER SIDE ACCESS AUTHORIZATION MODULE
+**
+**	Contains the means for checking the user access
+**	authorization for a file.
+**
+** IMPORTANT:
+**	Routines in this module use dynamic allocation, but free
+**	automatically all the memory reserved by them.
+**
+**	Therefore the caller never has to (and never should)
+**	free() any object returned by these functions.
+**
+**	Therefore also all the strings returned by this package
+**	are only valid until the next call to the same function
+**	is made. This approach is selected, because of the nature
+**	of access authorization: no string returned by the package
+**	needs to be valid longer than until the next call.
+**
+**	This also makes it easy to plug the AA package in:
+**	you don't have to ponder whether to free() something
+**	here or is it done somewhere else (because it is always
+**	done somewhere else).
+**
+**	The strings that the package needs to store are copied
+**	so the original strings given as parameters to AA
+**	functions may be freed or modified with no side effects.
+**
+**	The AA package does not free() anything else than what
+**	it has itself allocated.
+**
+** AUTHORS:
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**
+** HISTORY:
+**
+**
+** BUGS:
+**
+**
+*/
+
+#include <HTUtils.h>
+
+#include <HTString.h>
+#include <HTAccess.h>		/* HTSecure			*/
+#include <HTFile.h>		/* HTLocalName			*/
+#include <HTRules.h>		/*				*/
+#include <HTParse.h>		/* URL parsing function 	*/
+#include <HTList.h>		/* HTList object		*/
+
+#include <HTAAUtil.h>		/* AA common parts		*/
+#include <HTAuth.h>		/* Authentication		*/
+#include <HTACL.h>		/* Access Control List		*/
+#include <HTGroup.h>		/* Group handling		*/
+#include <HTAAProt.h>		/* Protection file parsing	*/
+#include <HTAAServ.h>		/* Implemented here		*/
+
+#include <LYLeaks.h>
+
+/*
+** Global variables
+*/
+PUBLIC time_t theTime;
+
+
+/*
+** Module-wide global variables
+*/
+PRIVATE FILE *	htaa_logfile	    = NULL;		/* Log file	      */
+PRIVATE HTAAUser *htaa_user = NULL;			/* Authenticated user */
+PRIVATE HTAAFailReasonType HTAAFailReason = HTAA_OK;	/* AA fail reason     */
+
+
+/* SERVER PUBLIC					HTAA_statusMessage()
+**		RETURN A STRING EXPLAINING ACCESS
+**		AUTHORIZATION FAILURE
+**		(Can be used in server reply status line
+**		 with 401/403 replies.)
+** ON EXIT:
+**	returns a string containing the error message
+**		corresponding to internal HTAAFailReason.
+*/
+PUBLIC char *HTAA_statusMessage NOARGS
+{
+    switch (HTAAFailReason) {
+
+    /* 401 cases */
+      case HTAA_NO_AUTH:
+	return "Unauthorized -- authentication failed";
+      case HTAA_NOT_MEMBER:
+	return "Unauthorized to access the document";
+
+    /* 403 cases */
+      case HTAA_BY_RULE:
+	return "Forbidden -- by rule";
+      case HTAA_IP_MASK:
+	return "Forbidden -- server refuses to serve to your IP address";
+      case HTAA_NO_ACL:
+      case HTAA_NO_ENTRY:
+	return "Forbidden -- access to file is never allowed";
+      case HTAA_SETUP_ERROR:
+	return "Forbidden -- server protection setup error";
+      case HTAA_DOTDOT:
+	return "Forbidden -- URL containing /../ disallowed";
+      case HTAA_HTBIN:
+	return "Forbidden -- /htbin feature not enabled on this server";
+
+    /* 404 cases */
+      case HTAA_NOT_FOUND:
+	return "Not found -- file doesn't exist or is read protected";
+
+    /* Success */
+      case HTAA_OK:
+	return "AA: Access should be ok but something went wrong";
+
+      case HTAA_OK_GATEWAY:
+	return "AA check bypassed (gatewaying) but something went wrong";
+
+    /* Others */
+      default:
+	return "Access denied -- unable to specify reason (bug)";
+
+    } /* switch */
+}
+
+
+PRIVATE char *status_name ARGS1(HTAAFailReasonType, reason)
+{
+    switch (reason) {
+
+    /* 401 cases */
+      case HTAA_NO_AUTH:
+	return "NO-AUTHENTICATION";
+      case HTAA_NOT_MEMBER:
+	return "NOT-AUTHORIZED";
+
+    /* 403 cases */
+      case HTAA_BY_RULE:
+	return "FORB-RULE";
+      case HTAA_IP_MASK:
+	return "FORB-IP";
+      case HTAA_NO_ACL:
+	return "NO-ACL-FILE";
+      case HTAA_NO_ENTRY:
+	return "NO-ACL-ENTRY";
+      case HTAA_SETUP_ERROR:
+	return "SETUP-ERROR";
+      case HTAA_DOTDOT:
+	return "SLASH-DOT-DOT";
+      case HTAA_HTBIN:
+	return "HTBIN-OFF";
+
+    /* 404 cases */
+      case HTAA_NOT_FOUND:
+	return "NOT-FOUND";
+
+    /* Success */
+      case HTAA_OK:
+	return "OK";
+      case HTAA_OK_GATEWAY:
+	return "OK-GATEWAY";
+
+    /* Others */
+      default:
+	return "SERVER-BUG";
+    } /* switch */
+}
+
+
+/* PRIVATE						check_uthorization()
+**		CHECK IF USER IS AUTHORIZED TO ACCESS A FILE
+** ON ENTRY:
+**	pathname	is the physical file pathname
+**			to access.
+**	method		method, e.g. METHOD_GET, METHOD_PUT, ...
+**	scheme		authentication scheme.
+**	scheme_specifics authentication string (or other
+**			scheme specific parameters, like
+**			Kerberos-ticket).
+**
+** ON EXIT:
+**	returns 	HTAA_OK on success.
+**			Otherwise the reason for failing.
+** NOTE:
+**	This function does not check whether the file
+**	exists or not -- so the status	404 Not found
+**	must be returned from somewhere else (this is
+**	to avoid unnecessary overhead of opening the
+**	file twice).
+*/
+PRIVATE HTAAFailReasonType check_authorization ARGS4(CONST char *,  pathname,
+						     HTAAMethod,    method,
+						     HTAAScheme,    scheme,
+						     char *, scheme_specifics)
+{
+    HTAAFailReasonType reason;
+    GroupDef *allowed_groups;
+    FILE *acl_file = NULL;
+    HTAAProt *prot = NULL;	/* Protection mode */
+
+    htaa_user = NULL;
+
+    if (!pathname) {
+	CTRACE(tfp, "HTAA_checkAuthorization: Forbidden by rule\n");
+	return HTAA_BY_RULE;
+    }
+    CTRACE(tfp, "%s `%s' %s %s\n",
+		"HTAA_checkAuthorization: translated path:",
+		pathname, "method:", HTAAMethod_name(method));
+
+    /*
+    ** Get protection setting (set up by callbacks from rule system)
+    ** NULL, if not protected by a "protect" rule.
+    */
+    prot = HTAA_getCurrentProtection();
+
+    /*
+    ** Check ACL existence
+    */
+    if (!(acl_file = HTAA_openAcl(pathname))) {
+	if (prot) { /* protect rule, but no ACL */
+	    if (prot->mask_group) {
+		/*
+		** Only mask enabled, check that
+		*/
+		GroupDefList *group_def_list =
+		    HTAA_readGroupFile(HTAssocList_lookup(prot->values,
+							  "group"));
+		/*
+		** Authenticate if authentication info given
+		*/
+		if (scheme != HTAA_UNKNOWN  &&	scheme != HTAA_NONE) {
+		    htaa_user = HTAA_authenticate(scheme,
+						  scheme_specifics,
+						  prot);
+		    CTRACE(tfp, "Authentication returned: %s\n",
+				(htaa_user ? htaa_user->username
+					   : "NOT-AUTHENTICATED"));
+		}
+		HTAA_resolveGroupReferences(prot->mask_group, group_def_list);
+		reason = HTAA_userAndInetInGroup(prot->mask_group,
+						 htaa_user
+						  ? htaa_user->username : "",
+						 HTClientHost,
+						 NULL);
+		if (reason != HTAA_OK) {
+		    CTRACE(tfp, "%s %s %s %s\n",
+				"HTAA_checkAuthorization: access denied",
+				"by mask (no ACL, only Protect rule)",
+				"host", HTClientHost);
+		} else {
+		    CTRACE(tfp, "%s %s %s %s\n",
+				"HTAA_checkAuthorization: request from",
+				HTClientHost,
+				"accepted by only mask match (no ACL, only",
+				"Protect rule, and only mask enabled)");
+		}
+		return reason;
+	    }
+	    else {	/* 403 Forbidden */
+		CTRACE(tfp, "%s %s\n",
+			    "HTAA_checkAuthorization: Protected, but",
+			    "no mask group nor ACL -- forbidden");
+		return HTAA_NO_ACL;
+	    }
+	}
+	else { /* No protect rule and no ACL => OK 200 */
+	    CTRACE(tfp, "HTAA_checkAuthorization: %s\n",
+			"no protect rule nor ACL -- ok\n");
+	    return HTAA_OK;
+	}
+    }
+
+    /*
+    ** Now we know that ACL exists
+    */
+    if (!prot) {		/* Not protected by "protect" rule */
+	CTRACE(tfp, "HTAA_checkAuthorization: default protection\n");
+	prot = HTAA_getDefaultProtection(); /* Also sets current protection */
+
+	if (!prot) {		/* @@ Default protection not set ?? */
+	    CTRACE(tfp, "%s %s\n",
+			"HTAA_checkAuthorization: default protection",
+			"not set (internal server error)!!");
+	    return HTAA_SETUP_ERROR;
+	}
+    }
+
+    /*
+    ** Now we know that document is protected and ACL exists.
+    ** Check against ACL entry.
+    */
+    {
+	GroupDefList *group_def_list =
+	    HTAA_readGroupFile(HTAssocList_lookup(prot->values, "group"));
+
+	/*
+	** Authenticate now that we know protection mode
+	*/
+	if (scheme != HTAA_UNKNOWN  &&	scheme != HTAA_NONE) {
+	    htaa_user = HTAA_authenticate(scheme,
+					  scheme_specifics,
+					  prot);
+	    CTRACE(tfp, "Authentication returned: %s\n",
+			(htaa_user
+			 ? htaa_user->username : "NOT-AUTHENTICATED"));
+	}
+	/*
+	** Check mask group
+	*/
+	if (prot->mask_group) {
+	    HTAA_resolveGroupReferences(prot->mask_group, group_def_list);
+	    reason=HTAA_userAndInetInGroup(prot->mask_group,
+					   htaa_user ? htaa_user->username : "",
+					   HTClientHost,
+					   NULL);
+	    if (reason != HTAA_OK) {
+		CTRACE(tfp, "%s %s %s\n",
+			    "HTAA_checkAuthorization: access denied",
+			    "by mask, host:", HTClientHost);
+		return reason;
+	    }
+	    else {
+		CTRACE(tfp, "%s %s %s %s %s\n",
+			    "HTAA_checkAuthorization: request from",
+			    HTClientHost,
+			    "accepted by just mask group match",
+			    "(no ACL, only Protect rule, and only",
+			    "mask enabled)");
+		/* And continue authorization checking */
+	    }
+	}
+	/*
+	** Get ACL entries; get first one first, the loop others
+	** Remember, allowed_groups is automatically freed by
+	** HTAA_getAclEntry().
+	*/
+	allowed_groups = HTAA_getAclEntry(acl_file, pathname, method);
+	if (!allowed_groups) {
+	    CTRACE(tfp, "%s `%s' %s\n",
+			"No entry for file", pathname, "in ACL");
+	    HTAA_closeAcl(acl_file);
+	    return HTAA_NO_ENTRY;  /* Forbidden -- no entry in the ACL */
+	}
+	else {
+	    do {
+		HTAA_resolveGroupReferences(allowed_groups, group_def_list);
+		reason = HTAA_userAndInetInGroup(allowed_groups,
+						 htaa_user
+						 ? htaa_user->username : "",
+						 HTClientHost,
+						 NULL);
+		if (reason == HTAA_OK) {
+		    HTAA_closeAcl(acl_file);
+		    return HTAA_OK;	/* OK */
+		}
+		allowed_groups = HTAA_getAclEntry(acl_file, pathname, method);
+	    } while (allowed_groups);
+	    HTAA_closeAcl(acl_file);
+	    return HTAA_NOT_MEMBER;	/* Unauthorized */
+	}
+    }
+}
+
+
+/* PUBLIC					      HTAA_checkAuthorization()
+**		CHECK IF USER IS AUTHORIZED TO ACCESS A FILE
+** ON ENTRY:
+**	url		is the document to be accessed.
+**	method_name	name of the method, e.g. "GET"
+**	scheme_name	authentication scheme name.
+**	scheme_specifics authentication string (or other
+**			scheme specific parameters, like
+**			Kerberos-ticket).
+**
+** ON EXIT:
+**	returns status codes uniform with those of HTTP:
+**	  200 OK	   if file access is ok.
+**	  401 Unauthorized if user is not authorized to
+**			   access the file.
+**	  403 Forbidden    if there is no entry for the
+**			   requested file in the ACL.
+**
+** NOTE:
+**	This function does not check whether the file
+**	exists or not -- so the status	404 Not found
+**	must be returned from somewhere else (this is
+**	to avoid unnecessary overhead of opening the
+**	file twice).
+**
+*/
+PUBLIC int HTAA_checkAuthorization ARGS4(CONST char *,	url,
+					 CONST char *,	method_name,
+					 CONST char *,	scheme_name,
+					 char *,	scheme_specifics)
+{
+    static char *pathname = NULL;
+    char *local_copy = NULL;
+    HTAAMethod method = HTAAMethod_enum(method_name);
+    HTAAScheme scheme = HTAAScheme_enum(scheme_name);
+
+    HTAAFailReason = HTAA_OK;
+
+    /*
+    ** Translate into absolute pathname, and
+    ** check for "protect" and "defprot" rules.
+    */
+    FREE(pathname);		/* From previous call	*/
+    StrAllocCopy(local_copy, url);
+    {
+	char *keywords = strchr(local_copy, '?');
+	if (keywords)
+	    *keywords = '\0';	/* Chop off keywords */
+    }
+    HTSimplify(local_copy);	/* Remove ".." etc. */
+
+    /* HTSimplify will leave in a "/../" at the top, which can
+    ** be a security hole.
+    */
+    if (strstr(local_copy, "/../")) {
+	CTRACE(tfp, "HTAA_checkAuthorization: %s (`%s')\n",
+		    "Illegal attempt to use /../", url);
+	HTAAFailReason = HTAA_DOTDOT;
+    }
+    else {
+	pathname = HTTranslate(local_copy); /* Translate rules even if */
+					    /* a /htbin call to set up */
+					    /* protections.	       */
+	if (0 == strncmp(local_copy, "/htbin/", 7)) {
+	    if (!HTBinDir)
+		HTAAFailReason = HTAA_HTBIN;
+	    else {
+		char *end = strchr(local_copy+7, '/');
+		if (end)
+		    *end = '\0';
+		FREE(pathname);
+		pathname=(char*)malloc(strlen(HTBinDir)+strlen(local_copy)+1);
+		strcpy(pathname, HTBinDir);
+		strcat(pathname, local_copy+6);
+	    }
+	}
+
+	if (!pathname) {		/* Forbidden by rule */
+	    CTRACE(tfp, "HTAA_checkAuthorization: Forbidden by rule\n");
+	    HTAAFailReason = HTAA_BY_RULE;
+	}
+	else if (HTAAFailReason != HTAA_HTBIN) {
+	    /* pathname != NULL */
+	    char *acc_method = HTParse(pathname, "", PARSE_ACCESS);
+	    if (!*acc_method || 0 == strcmp(acc_method,"file")) { /*Local file, do AA*/
+		if (!HTSecure && 0 != strncmp(local_copy, "/htbin/", 7)) {
+		    char *localname = HTLocalName(pathname);
+		    FREE(pathname);
+		    pathname = localname;
+		}
+		HTAAFailReason = check_authorization(pathname, method,
+						     scheme, scheme_specifics);
+	    }
+	    else {  /* Not local access */
+		HTAAFailReason = HTAA_OK_GATEWAY;
+		CTRACE(tfp, "HTAA_checkAuthorization: %s (%s access)\n",
+			    "Gatewaying -- skipping authorization check",
+			    acc_method);
+	    }
+	} /* pathname */
+    }
+    FREE(local_copy);
+
+    if (htaa_logfile) {
+	time(&theTime);
+	fprintf(htaa_logfile, "%24.24s %s %s %s %s %s\n",
+		ctime(&theTime),
+		HTClientHost ? HTClientHost : "local",
+		method_name,
+		url,
+		status_name(HTAAFailReason),
+		htaa_user && htaa_user->username
+		? htaa_user->username : "");
+	fflush(htaa_logfile);	/* Actually update it on disk */
+	CTRACE(tfp, "Log: %24.24s %s %s %s %s %s\n",
+		    ctime(&theTime),
+		    HTClientHost ? HTClientHost : "local",
+		    method_name,
+		    url,
+		    status_name(HTAAFailReason),
+		    htaa_user && htaa_user->username
+		    ? htaa_user->username : "");
+    }
+
+    switch (HTAAFailReason) {
+
+      case HTAA_NO_AUTH:
+      case HTAA_NOT_MEMBER:
+	return 401;
+
+      case HTAA_BY_RULE:
+      case HTAA_IP_MASK:
+      case HTAA_NO_ACL:
+      case HTAA_NO_ENTRY:
+      case HTAA_SETUP_ERROR:
+      case HTAA_DOTDOT:
+      case HTAA_HTBIN:
+	return 403;
+
+      case HTAA_NOT_FOUND:
+	return 404;
+
+      case HTAA_OK:
+      case HTAA_OK_GATEWAY:
+	return 200;
+
+      default:
+	return 500;
+    } /* switch */
+}
+
+
+/* PRIVATE					compose_scheme_specifics()
+**		COMPOSE SCHEME-SPECIFIC PARAMETERS
+**		TO BE SENT ALONG WITH SERVER REPLY
+**		IN THE WWW-Authenticate: FIELD.
+** ON ENTRY:
+**	scheme		is the authentication scheme for which
+**			parameters are asked for.
+**	prot		protection setup structure.
+**
+** ON EXIT:
+**	returns 	scheme specific parameters in an
+**			auto-freed string.
+*/
+PRIVATE char *compose_scheme_specifics ARGS2(HTAAScheme,	scheme,
+					     HTAAProt *,	prot)
+{
+    static char *result = NULL;
+
+    FREE(result);	/* From previous call */
+
+    switch (scheme) {
+      case HTAA_BASIC:
+	{
+	    char *realm = HTAssocList_lookup(prot->values, "server");
+	    result = (char*)malloc(60);
+	    sprintf(result, "realm=\"%s\"",
+		    (realm ? realm : "UNKNOWN"));
+	    return result;
+	}
+
+      case HTAA_PUBKEY:
+	{
+	    char *realm = HTAssocList_lookup(prot->values, "server");
+	    result = (char*)malloc(200);
+	    sprintf(result, "realm=\"%s\", key=\"%s\"",
+		    (realm ? realm : "UNKNOWN"),
+		    "PUBKEY-NOT-IMPLEMENTED");
+	    return result;
+	}
+      default:
+	return NULL;
+    }
+}
+
+
+/* SERVER PUBLIC				    HTAA_composeAuthHeaders()
+**		COMPOSE WWW-Authenticate: HEADER LINES
+**		INDICATING VALID AUTHENTICATION SCHEMES
+**		FOR THE REQUESTED DOCUMENT
+** ON ENTRY:
+**	No parameters, but HTAA_checkAuthorization() must
+**	just before have failed because a wrong (or none)
+**	authentication scheme was used.
+**
+** ON EXIT:
+**	returns a buffer containing all the WWW-Authenticate:
+**		fields including CRLFs (this buffer is auto-freed).
+**		NULL, if authentication won't help in accessing
+**		the requested document.
+**
+*/
+PUBLIC char *HTAA_composeAuthHeaders NOARGS
+{
+    static char *result = NULL;
+    int  n;
+    char *scheme_name;
+    char *scheme_params;
+    HTAAProt *prot = HTAA_getCurrentProtection();
+
+    if (!prot) {
+	CTRACE(tfp, "%s %s\n",
+		    "HTAA_composeAuthHeaders: Document not protected",
+		    "-- why was this function called??");
+	return NULL;
+    } else {
+	CTRACE(tfp, "HTAA_composeAuthHeaders: for file `%s'\n",
+		    prot->filename);
+    }
+
+    FREE(result);	/* From previous call */
+    if (!(result = (char*)malloc(4096)))	/* @@ */
+	outofmem(__FILE__, "HTAA_composeAuthHeaders");
+    *result = '\0';
+
+    for (n = 0; n < (int) HTAA_MAX_SCHEMES; n++) {
+	HTAAScheme scheme = (HTAAScheme) n;
+	if (-1 < HTList_indexOf(prot->valid_schemes, (void*)scheme)) {
+	    if ((scheme_name = HTAAScheme_name(scheme))) {
+		scheme_params = compose_scheme_specifics(scheme,prot);
+		strcat(result, "WWW-Authenticate: ");
+		strcat(result, scheme_name);
+		if (scheme_params) {
+		    strcat(result, " ");
+		    strcat(result, scheme_params);
+		}
+		strcat(result, "\r\n");
+	    } /* scheme name found */
+	    else {
+		CTRACE(tfp, "HTAA_composeAuthHeaders: %s %d\n",
+			    "No name found for scheme number", scheme);
+	    }
+	} /* scheme valid for requested document */
+    } /* for every scheme */
+
+    return result;
+}
+
+
+/* PUBLIC						HTAA_startLogging()
+**		START UP ACCESS AUTHORIZATION LOGGING
+** ON ENTRY:
+**	fp	is the open log file.
+**
+*/
+PUBLIC void HTAA_startLogging ARGS1(FILE *, fp)
+{
+    htaa_logfile = fp;
+}
+
