ftp.nice.ch/pub/next/unix/network/www/httpd.1.5-export.NIHS.bs.gnutar.gz#/httpd_1.5-export/src/http_access.c

This is http_access.c in view mode; [Download] [Up]

/************************************************************************
 * NCSA HTTPd Server
 * Software Development Group
 * National Center for Supercomputing Applications
 * University of Illinois at Urbana-Champaign
 * 605 E. Springfield, Champaign, IL 61820
 * httpd@ncsa.uiuc.edu
 *
 * Copyright  (C)  1995, Board of Trustees of the University of Illinois
 *
 ************************************************************************
 *
 * http_access.c,v 1.67 1995/11/03 21:56:54 blong Exp
 *
 ************************************************************************
 * 
 * http_access: Security options etc.
 *
 * Based on NCSA HTTPd 1.3 by Rob McCool
 *
 *  03-12-95 blong
 *     Added patch to fix ALLOW_THEN_DENY
 * 
 *  10-02-95 blong
 *	Added patch by Maurizio Codogno (mau@beatles.cselt.stet.it) to
 *	allow or deny hosts using a LOCAL keyword which matches all hosts
 *	without a dot in their name
 */


#include "config.h"
#include "portability.h"

#include <stdio.h>
#ifndef NO_STDLIB_H 
# include <stdlib.h>
#endif /* NO_STDLIB_H */
#include <string.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include "constants.h"
#include "http_access.h"
#include "http_request.h"
#include "http_config.h"
#include "http_auth.h"
#include "http_mime.h"
#include "http_log.h"
#include "util.h"

#ifdef DIGEST_AUTH
int client_accepts_digest;
int assume_digest_support;
#endif /* DIGEST_AUTH */


/*
 * Modified this bad boy so he wouldn't
 * match things like "allow csa.ncsa.uiuc.edu" and
 * a ncsa.uiuc.edu. SSG 7/10/95
 */
int in_domain(char *domain, char *what) 
{
    int dl=strlen(domain);
    int wl=strlen(what);

    if(((wl-dl) >= 0) && !strcmp(domain,&what[wl-dl])) {
	if (((wl - dl) > 0) && (*domain != '.') && (what[wl-dl-1] != '.')) 
	    return 0;   /* only a partial match - SSG */
	return 1;
    }
    else {
        return 0;
    }
}

int in_ip(char *domain, char *what) 
{
    return(!strncmp(domain,what,strlen(domain)));
}

/* find_allow()
 * Hunts down list of allowed hosts and returns 1 if allowed, 0 if not
 * As soon as it finds an allow that matches, it returns 1
 */

int find_allow(per_request *reqInfo, int x) 
{
    register int y;

    /* If no allows are specified, then allow */
    if(sec[x].num_allow[reqInfo->method] == 0)
        return FA_ALLOW;

    for(y=0;y<sec[x].num_allow[reqInfo->method];y++) {
        if(!strcmp("all",sec[x].allow[reqInfo->method][y])) 
            return FA_ALLOW;
#ifdef LOCALHACK
        if((!strcmp("LOCAL",sec[x].allow[reqInfo->method][y])) &&
                 reqInfo->remote_host &&
		  (ind(reqInfo->remote_host,'.') == -1)) 
	 {
	    reqInfo->bSatisfiedDomain = TRUE;
	    return FA_ALLOW;
         }
#endif /* LOCALHACK */
        if(in_ip(sec[x].allow[reqInfo->method][y],reqInfo->remote_ip))
	  {
            reqInfo->bSatisfiedDomain = TRUE;
	    return FA_ALLOW;
	  }
	/* If we haven't done a lookup, and the DNS type is Minimum, then
	 * we do a lookup now
	 */
	if (!reqInfo->dns_host_lookup && 
	   (reqInfo->hostInfo->dns_mode == DNS_MIN))
	     get_remote_host_min(reqInfo);
        if(reqInfo->remote_host) {
          if(in_domain(sec[x].allow[reqInfo->method][y],reqInfo->remote_host))
	    {
              reqInfo->bSatisfiedDomain = TRUE;
	      return FA_ALLOW;
	    }
        }
    }
    /* Default is to deny */
    return FA_DENY;
}

/* find_deny()
 * Hunts down list of denied hosts and returns 0 if denied, 1 if not
 * As soon as it finds a deny that matches, it returns 0
 */
int find_deny(per_request *reqInfo, int x) 
{
    register int y;

    /* If there aren't any denies, then it is allowed 
     */
    if(sec[x].num_deny[reqInfo->method] == 0)
        return FA_ALLOW;

    for(y=0;y<sec[x].num_deny[reqInfo->method];y++) {
        if(!strcmp("all",sec[x].deny[reqInfo->method][y])) 
	  {
	    reqInfo->bSatisfiedDomain = FALSE;
	    return FA_DENY;
 	  }
#ifdef LOCALHACK
       if((!strcmp("LOCAL",sec[x].allow[reqInfo->method][y])) &&
		 reqInfo->remote_host &&
                 (ind(reqInfo->remote_host,'.') == -1))
	 {
             reqInfo->bSatisfiedDomain = FALSE;
	     return FA_DENY;
	 }
#endif /* LOCALHACK */
        if(in_ip(sec[x].deny[reqInfo->method][y],reqInfo->remote_ip))
	  {
	    reqInfo->bSatisfiedDomain = FALSE;
	    return FA_DENY;
	  }
	if (!reqInfo->dns_host_lookup && 
	   (reqInfo->hostInfo->dns_mode == DNS_MIN))
	     get_remote_host_min(reqInfo);
        if(reqInfo->remote_host) {
          if(in_domain(sec[x].deny[reqInfo->method][y],reqInfo->remote_host))
	      {
		reqInfo->bSatisfiedDomain = FALSE;
		return FA_DENY;
	      }
	}
    }
    /* Default is to allow */
    reqInfo->bSatisfiedDomain = TRUE;
    return FA_ALLOW;
}

void check_dir_access(per_request *reqInfo,int x, 
		      int *allow, int *allow_options) 
{
    if(sec[x].auth_name[0]) 
	reqInfo->auth_name = sec[x].auth_name;
    if(sec[x].auth_pwfile[0]) {
        reqInfo->auth_pwfile = sec[x].auth_pwfile;
	reqInfo->auth_pwfile_type = sec[x].auth_pwfile_type;
    }
    if(sec[x].auth_grpfile[0]) {
        reqInfo->auth_grpfile = sec[x].auth_grpfile;
        reqInfo->auth_grpfile_type = sec[x].auth_grpfile_type;
    }
#ifdef DIGEST_AUTH
    if(sec[x].auth_digestfile[0]) {
        reqInfo->auth_digestfile = sec[x].auth_digestfile;
        reqInfo->auth_digestfile_type = sec[x].auth_digestfile_type;
    }
#endif /* DIGEST_AUTH */
    if (sec[x].auth_type[0])
	strcpy(reqInfo->auth_type,sec[x].auth_type);

    *allow=0;
    if(sec[x].order[reqInfo->method] == ALLOW_THEN_DENY) {
        *allow = find_allow(reqInfo,x);
        *allow = find_deny(reqInfo,x);
    } 
    else if(sec[x].order[reqInfo->method] == DENY_THEN_ALLOW) {
        *allow = find_deny(reqInfo,x);
        *allow = find_allow(reqInfo,x);
    }
    else
        *allow = find_allow(reqInfo,x) && !find_deny(reqInfo,x);
   
    if(sec[x].num_auth[reqInfo->method])
        *allow_options=x;
}

void evaluate_access(per_request *reqInfo,struct stat *finfo,int *allow,
		     char *allow_options) 
{
    int will_allow, need_auth, num_dirs;
    char opts[MAX_STRING_LEN], override[MAX_STRING_LEN];
    char path[MAX_STRING_LEN], d[MAX_STRING_LEN];
    char errstr[MAX_STRING_LEN];
    register int x,y,z,n;

    if(S_ISDIR(finfo->st_mode)) 
      strncpy_dir(path,reqInfo->filename,MAX_STRING_LEN);
    else lim_strcpy(path,reqInfo->filename,MAX_STRING_LEN);
    
    no2slash(path);

    num_dirs = count_dirs(path);
    will_allow = 1; 
    need_auth = -1;

    user[0] = '\0';
    groupname[0] = '\0';
    reset_mime_vars();

    for(x=0;x<num_dirs;x++) {
        opts[x] = OPT_ALL;
        override[x] = OR_ALL;
    }

    /* assume not domain restricted */
    reqInfo->bSatisfiedDomain = 0;
    
    n=num_dirs-1;
    for(x=0;x<num_sec;x++) {
        if(is_matchexp(sec[x].d)) {
            if(!strcmp_match(path,sec[x].d)) {
                for(y=0;y<num_dirs;y++) {
                    if(!(sec[x].opts & OPT_UNSET))
                        opts[y] = sec[x].opts;
                    override[y] = sec[x].override;
                }
            }
            check_dir_access(reqInfo,x,&will_allow,&need_auth);
        }
        else if(!strncmp(path,sec[x].d,strlen(sec[x].d))) {
            for(y=count_dirs(sec[x].d) - 1;y<num_dirs;y++) {
                if(!(sec[x].opts & OPT_UNSET))
                    opts[y] = sec[x].opts;
                override[y] = sec[x].override;
            }
            check_dir_access(reqInfo,x,&will_allow,&need_auth);
        }
    }
    if((override[n]) || (!(opts[n] & OPT_SYM_LINKS)) || 
       (opts[n] & OPT_SYM_OWNER)) {
	for(x=0;x<num_dirs;x++) {
	    y = num_sec;
	    make_dirstr(path,x+1,d);
	    if((!(opts[x] & OPT_SYM_LINKS)) || (opts[x] & OPT_SYM_OWNER)) {
		struct stat lfi,fi;
		
		if(lstat(d,&lfi) != 0)
		{
		    sprintf(errstr,"httpd: can't lstat %s, errno = %d",d, errno);
		    log_error(errstr,reqInfo->hostInfo->error_log);
		    *allow=0;
		    *allow_options = OPT_NONE;
		    return;
		}
		if(!(S_ISDIR(lfi.st_mode))) {
		    if(opts[x] & OPT_SYM_OWNER) {
			if(stat(d,&fi) != 0)
			{
			    sprintf(errstr,"httpd: can't stat %s, errno = %d",d, errno);
			    log_error(errstr,reqInfo->hostInfo->error_log);
			    *allow=0;
			    *allow_options = OPT_NONE;
			    return;
			}
			if(fi.st_uid != lfi.st_uid)
			    goto bong;
		    }
		    else {
		      bong:
			sprintf(errstr,"httpd: will not follow link %s",d);
			log_error(errstr,reqInfo->hostInfo->error_log);
			*allow=0;
			*allow_options = OPT_NONE;
			return;
		    }
		}
	    }
	    if(override[x]) {
		parse_htaccess(reqInfo,d,override[x]);
		if(num_sec != y) {
		    for(z=count_dirs(sec[y].d) - 1;z<num_dirs;z++) {
			if(!(sec[y].opts & OPT_UNSET))
			    opts[z] = sec[y].opts;
			override[z] = sec[y].override;
		    }
		    if (sec[y].num_auth[reqInfo->method] > 0 || 
			sec[y].num_allow[reqInfo->method] > 0)
			check_dir_access(reqInfo,y,&will_allow,&need_auth);
		}
	    }
	}
    }
    if((!(S_ISDIR(finfo->st_mode))) && 
       ((!(opts[n] & OPT_SYM_LINKS)) || (opts[n] & OPT_SYM_OWNER))) {
        struct stat fi,lfi;
        if(lstat(path,&fi)!=0)
	{
	    sprintf(errstr,"httpd: can't lstat %s, errno = %d",path, errno);
	    log_error(errstr,reqInfo->hostInfo->error_log);
	    *allow=0;
	    *allow_options = OPT_NONE;
	    return;
	}
        if(!(S_ISREG(fi.st_mode))) {
            if(opts[n] & OPT_SYM_OWNER) {
                if(stat(path,&lfi)!=0)
		{
		    sprintf(errstr,"httpd: can't stat %s, errno = %d",path, errno);
		    log_error(errstr,reqInfo->hostInfo->error_log);
		    *allow=0;
		    *allow_options = OPT_NONE;
		    return;
		}
                if(fi.st_uid != lfi.st_uid)
                    goto gong;
            }
            else {
              gong:
                sprintf(errstr,"httpd: will not follow link %s",path);
                log_error(errstr,reqInfo->hostInfo->error_log);
                *allow=0;
                *allow_options = OPT_NONE;
                return;
            }
        }
    }
    *allow = will_allow;
    if(will_allow) {
        *allow_options = opts[num_dirs-1];
        if ((need_auth >= 0) && !sec[need_auth].bSatisfy) {
	    reqInfo->bSatisfiedDomain = 0;
	    check_auth(reqInfo,&sec[need_auth], auth_line);
	}
    } else if (need_auth >= 0 && sec[need_auth].bSatisfy) {
	check_auth(reqInfo,&sec[need_auth], auth_line);
	*allow_options = opts[num_dirs-1];
	*allow = 1;
    }
    else *allow_options = 0;
}

void kill_security() 
{
    register int x,y,m;

    for(x=0;x<num_sec;x++) {
        /*free(sec[x].d);*/
        for(m=0;m<METHODS;m++) {
            for(y=0;y<sec[x].num_allow[m];y++)
                free(sec[x].allow[m][y]);
            for(y=0;y<sec[x].num_deny[m];y++)
                free(sec[x].deny[m][y]);
            for(y=0;y<sec[x].num_auth[m];y++)
                free(sec[x].auth[m][y]);
        }
/*        if(sec[x].auth_type)
            free(sec[x].auth_type);
        if(sec[x].auth_name)
            free(sec[x].auth_name);
        if(sec[x].auth_pwfile)
            free(sec[x].auth_pwfile);
        if(sec[x].auth_grpfile)
            free(sec[x].auth_grpfile);
#ifdef DIGEST_AUTH
        if(sec[x].auth_digestfile)
            free(sec[x].auth_digestfile);
#endif*/ /* DIGEST_AUTH */
    }
}


/* This function should reset the security data structure to contain only
   the information given in the access configuration file.  It should be 
   called after any transactions */

void reset_security() 
{
    register int x,y,m;

    for(x=num_sec_config;x<num_sec;x++) {
        /*free(sec[x].d);*/
        for(m=0;m<METHODS;m++) {
            for(y=0;y<sec[x].num_allow[m];y++)
                free(sec[x].allow[m][y]);
            for(y=0;y<sec[x].num_deny[m];y++)
                free(sec[x].deny[m][y]);
            for(y=0;y<sec[x].num_auth[m];y++)
                free(sec[x].auth[m][y]);
        }
/*        if(sec[x].auth_type)
            free(sec[x].auth_type);
        if(sec[x].auth_name)
            free(sec[x].auth_name);
        if(sec[x].auth_pwfile)
            free(sec[x].auth_pwfile);
        if(sec[x].auth_grpfile)
            free(sec[x].auth_grpfile);
#ifdef DIGEST_AUTH
        if(sec[x].auth_digestfile)
            free(sec[x].auth_digestfile);
#endif*/ /* DIGEST_AUTH */
    }

   num_sec = num_sec_config;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.