/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* .
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
/*
* Security options etc.
*
* Module derived from code originally written by Rob McCool
*
*/
#include "httpd.h"
#include "http_core.h"
#include "http_config.h"
#include "http_log.h"
#include "http_request.h"
/*gaa-api includes*/
#include "gaa.h"
#include "gaa_simple.h"
#include "gaa_debug.h"
#include "gaa_util.h"
#include "gaa_private.h"
//for gaaint_list_entry
#include "gaa_syspolicy.h"
//for PATHNAME_SIZE
//#include "time_parser.h"
//#include "host_parser.h"
#include "id_parser.h"
//my own parser Li Zhou
enum allowdeny_type {
T_ENV,
T_ALL,
T_IP,
T_HOST,
T_FAIL
};
typedef struct {
int limited;
union {
char *from;
struct {
unsigned long net;
unsigned long mask;
} ip;
} x;
enum allowdeny_type type;
} allowdeny;
/* things in the 'order' array */
#define DENY_THEN_ALLOW 0
#define ALLOW_THEN_DENY 1
#define MUTUAL_FAILURE 2
//Added by Li Zhou
#define GAA_EACL_INVALID 101
#define GAA_MODE_INVALID 101
//user defined error code
#define GAA_MODE_BOTH 0
#define GAA_MODE_EITHER 1
#define GAA_MODE_REPLACE 2
#define GAA_MODE_VALID 3
typedef struct {
int limited;
char *file;
} eacl_conf;
#define EACL_THEN_APACHE 0
#define APACHE_THEN_EACL 1
#define MODE_EXPAND 0
#define MODE_NARROW 1
#define MODE_EXACT 2
#define MODE_UNDEFINED 3
typedef struct {
int limited;
int order;
int apache_mode;
} gaa_conf;
typedef struct {
int order[METHODS];
array_header *allows;
array_header *denys;
array_header *eacl; //Added by Li Zhou
array_header *gaa; //Added by Li Zhou
} access_dir_conf;
module MODULE_VAR_EXPORT access_module;
static void *create_access_dir_config(pool *p, char *dummy)
{
access_dir_conf *conf =
(access_dir_conf *) ap_pcalloc(p, sizeof(access_dir_conf));
int i;
for (i = 0; i < METHODS; ++i)
conf->order[i] = DENY_THEN_ALLOW;
conf->allows = ap_make_array(p, 1, sizeof(allowdeny));
conf->denys = ap_make_array(p, 1, sizeof(allowdeny));
//added by zhou li
conf->eacl = ap_make_array(p,1,sizeof(eacl_conf));
conf->gaa = ap_make_array(p,1,sizeof(gaa_conf));
return (void *) conf;
}
static const char *order(cmd_parms *cmd, void *dv, char *arg)
{
access_dir_conf *d = (access_dir_conf *) dv;
int i, o;
if (!strcasecmp(arg, "allow,deny"))
o = ALLOW_THEN_DENY;
else if (!strcasecmp(arg, "deny,allow"))
o = DENY_THEN_ALLOW;
else if (!strcasecmp(arg, "mutual-failure"))
o = MUTUAL_FAILURE;
else
return "unknown order";
for (i = 0; i < METHODS; ++i)
if (cmd->limited & (1 << i))
d->order[i] = o;
return NULL;
}
static int is_ip(const char *host)
{
while ((*host == '.') || ap_isdigit(*host))
host++;
return (*host == '\0');
}
static const char *allow_cmd(cmd_parms *cmd, void *dv, char *from, char *where)
{
access_dir_conf *d = (access_dir_conf *) dv;
allowdeny *a;
char *s;
if (strcasecmp(from, "from"))
return "allow and deny must be followed by 'from'";
a = (allowdeny *) ap_push_array(cmd->info ? d->allows : d->denys);
a->x.from = where;
a->limited = cmd->limited;
if (!strncasecmp(where, "env=", 4)) {
a->type = T_ENV;
a->x.from += 4;
}
else if (!strcasecmp(where, "all")) {
a->type = T_ALL;
}
else if ((s = strchr(where, '/'))) {
unsigned long mask;
a->type = T_IP;
/* trample on where, we won't be using it any more */
*s++ = '\0';
if (!is_ip(where)
|| (a->x.ip.net = ap_inet_addr(where)) == INADDR_NONE) {
a->type = T_FAIL;
return "syntax error in network portion of network/netmask";
}
/* is_ip just tests if it matches [\d.]+ */
if (!is_ip(s)) {
a->type = T_FAIL;
return "syntax error in mask portion of network/netmask";
}
/* is it in /a.b.c.d form? */
if (strchr(s, '.')) {
mask = ap_inet_addr(s);
if (mask == INADDR_NONE) {
a->type = T_FAIL;
return "syntax error in mask portion of network/netmask";
}
}
else {
/* assume it's in /nnn form */
mask = atoi(s);
if (mask > 32 || mask <= 0) {
a->type = T_FAIL;
return "invalid mask in network/netmask";
}
mask = 0xFFFFFFFFUL << (32 - mask);
mask = htonl(mask);
}
a->x.ip.mask = mask;
a->x.ip.net = (a->x.ip.net & mask); /* pjr - This fixes PR 4770 */
}
else if (ap_isdigit(*where) && is_ip(where)) {
/* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */
int shift;
char *t;
int octet;
a->type = T_IP;
/* parse components */
s = where;
a->x.ip.net = 0;
a->x.ip.mask = 0;
shift = 24;
while (*s) {
t = s;
if (!ap_isdigit(*t)) {
a->type = T_FAIL;
return "invalid ip address";
}
while (ap_isdigit(*t)) {
++t;
}
if (*t == '.') {
*t++ = 0;
}
else if (*t) {
a->type = T_FAIL;
return "invalid ip address";
}
if (shift < 0) {
return "invalid ip address, only 4 octets allowed";
}
octet = atoi(s);
if (octet < 0 || octet > 255) {
a->type = T_FAIL;
return "each octet must be between 0 and 255 inclusive";
}
a->x.ip.net |= octet << shift;
a->x.ip.mask |= 0xFFUL << shift;
s = t;
shift -= 8;
}
a->x.ip.net = ntohl(a->x.ip.net);
a->x.ip.mask = ntohl(a->x.ip.mask);
}
else {
a->type = T_HOST;
}
return NULL;
}
//Added By Li Zhou
static const char *eacl_file_cmd(cmd_parms *cmd, void *dv, char *arg)
{
access_dir_conf *d = (access_dir_conf *) dv;
eacl_conf *gc;
gc=(eacl_conf *)ap_push_array(d->eacl);
gc->file=arg;
gc->limited=cmd->limited;
/*
if (mode==0)
{
gc->mode=GAA_MODE_BOTH;
}
else if (strcasecmp(mode,"")==0 || strcasecmp(mode,"both")==0)
{
gc->mode=GAA_MODE_BOTH;
}
else if (strcasecmp(mode,"either")==0)
{
gc->mode=GAA_MODE_EITHER;
}
else if (strcasecmp(mode,"replace")==0)
{
gc->mode=GAA_MODE_REPLACE;
}
else if (strcasecmp(mode,"valid")==0)
{
gc->mode=GAA_MODE_VALID;
}
else
{
gc->mode=GAA_MODE_BOTH;
return "The second argument must be null or one of {both,either,replace,valid}";
}
*/
return NULL;
}
static const char *gaa_mode_cmd(cmd_parms *cmd, void *dv, char *order, char *mode)
{
access_dir_conf *d = (access_dir_conf *) dv;
gaa_conf *gc;
gc=(gaa_conf *)ap_push_array(d->gaa);
gc->limited=cmd->limited;
if (strcasecmp(order,"eacl,apache")==0)
{
gc->order=EACL_THEN_APACHE;
}
else if (strcasecmp(order,"apache,eacl")==0)
{
gc->order=APACHE_THEN_EACL;
}
else return "Invalid order, must be 'eacl,apache' or 'apache,eacl'";
if (mode==0 || strcasecmp(mode,"")==0)
{
gc->apache_mode=MODE_UNDEFINED;
}
else if (strcasecmp(mode,"expand")==0)
{
gc->apache_mode=MODE_EXPAND;
}
else if (strcasecmp(mode,"narrow")==0)
{
gc->apache_mode=MODE_NARROW;
}
else if (strcasecmp(mode,"exact")==0)
{
gc->apache_mode=MODE_EXACT;
}
else
{
return "Invalid apache mode, must be 'expand', 'narrow', 'exact' or null";
}
return NULL;
}
static char its_an_allow;
static const command_rec access_cmds[] =
{
{"order", order, NULL, OR_LIMIT, TAKE1,
"'allow,deny', 'deny,allow', or 'mutual-failure'"},
{"allow", allow_cmd, &its_an_allow, OR_LIMIT, ITERATE2,
"'from' followed by hostnames or IP-address wildcards"},
{"deny", allow_cmd, NULL, OR_LIMIT, ITERATE2,
"'from' followed by hostnames or IP-address wildcards"},
{"eaclfile",eacl_file_cmd, NULL, OR_LIMIT, TAKE1,
"'eaclfile' followed by file name(path) and mode of EACL policy file"}, //Added by Li Zhou
{"gaaorder",gaa_mode_cmd, NULL, OR_LIMIT, TAKE12,
"'gaaorder followed by ('eacl,apache' | 'apache,eacl' ) ['expand'|'narrow'|'exact']"},
{NULL}
};
static int in_domain(const char *domain, const char *what)
{
int dl = strlen(domain);
int wl = strlen(what);
if ((wl - dl) >= 0) {
if (strcasecmp(domain, &what[wl - dl]) != 0)
return 0;
/* Make sure we matched an *entire* subdomain --- if the user
* said 'allow from good.com', we don't want people from nogood.com
* to be able to get in.
*/
if (wl == dl)
return 1; /* matched whole thing */
else
return (domain[0] == '.' || what[wl - dl - 1] == '.');
}
else
return 0;
}
static int find_allowdeny(request_rec *r, array_header *a, int method)
{
allowdeny *ap = (allowdeny *) a->elts;
int mmask = (1 << method);
int i;
int gothost = 0;
const char *remotehost = NULL;
for (i = 0; i < a->nelts; ++i) {
if (!(mmask & ap[i].limited))
continue;
switch (ap[i].type) {
case T_ENV:
if (ap_table_get(r->subprocess_env, ap[i].x.from)) {
return 1;
}
break;
case T_ALL:
return 1;
case T_IP:
if (ap[i].x.ip.net != INADDR_NONE
&& (r->connection->remote_addr.sin_addr.s_addr
& ap[i].x.ip.mask) == ap[i].x.ip.net) {
return 1;
}
break;
case T_HOST:
if (!gothost) {
remotehost = ap_get_remote_host(r->connection, r->per_dir_config,
REMOTE_DOUBLE_REV);
if ((remotehost == NULL) || is_ip(remotehost))
gothost = 1;
else
gothost = 2;
}
if ((gothost == 2) && in_domain(ap[i].x.from, remotehost))
return 1;
break;
case T_FAIL:
/* do nothing? */
break;
}
}
return 0;
}
#define ZHOULI_DEBUG
# ifdef ZHOULI_DEBUG
# define LOG1(a) ap_log_error(APLOG_MARK,APLOG_NOTICE|APLOG_NOERRNO,(r)?r->server:0,a)
# define LOG2(a,b) ap_log_error(APLOG_MARK,APLOG_NOTICE|APLOG_NOERRNO,(r)?r->server:0,a,b)
# define LOG3(a,b,c) ap_log_error(APLOG_MARK,APLOG_NOTICE|APLOG_NOERRNO,(r)?r->server:0,a,b,c)
# define LOG4(a,b,c,d) ap_log_error(APLOG_MARK,APLOG_NOTICE|APLOG_NOERRNO,(r)?r->server:0,a,b,c,d)
# define LOG5(a,b,c,d,e) ap_log_error(APLOG_MARK,APLOG_NOTICE|APLOG_NOERRNO,(r)?r->server:0,a,b,c,d,e)
# else /* !_DEBUG */
# define LOG1(a)
# define LOG2(a,b)
# define LOG3(a,b,c)
# define LOG4(a,b,c,d)
# define LOG5(a,b,c,d,e)
# endif /* _DEBUG */
//added by zhoul
static int find_gaamode(request_rec *r, array_header *a, int method)
{
gaa_conf *gc= (gaa_conf *) a->elts;
int mmask = (1 << method);
int i;
//LOG2("Gaa Count %d", a->nelts);
for (i=0;inelts;++i)
{
//LOG4("Gaa Section: %d, %d, %s",gc[i].mode, gc[i].limited, gc[i].file);
if (mmask & gc[i].limited)
{
return gc[i].apache_mode;
}
}
return MODE_UNDEFINED;
}
static int find_gaaorder(request_rec *r, array_header *a, int method)
{
gaa_conf *gc= (gaa_conf *) a->elts;
int mmask = (1 << method);
int i;
//LOG2("Gaa Count %d", a->nelts);
for (i=0;inelts;++i)
{
//LOG4("Gaa Section: %d, %d, %s",gc[i].mode, gc[i].limited, gc[i].file);
if (mmask & gc[i].limited)
{
return gc[i].order;
}
}
return EACL_THEN_APACHE;
}
static char *find_eaclfile(request_rec *r, array_header *a, int method)
{
eacl_conf *gc= (eacl_conf *) a->elts;
int mmask = (1 << method);
int i;
char *file=NULL;
//LOG2("Gaa Count %d", a->nelts);
for (i=0;inelts;++i)
{
//LOG4("Gaa Section: %d, %d, %s",gc[i].mode, gc[i].limited, gc[i].file);
if (mmask & gc[i].limited)
{
file=gc[i].file;
}
}
return file;
}
typedef struct {
access_dir_conf *raw_policy;
request_rec *record;
} cb_params;
static cb_params cbp;
/*
gaa_status cb_check_cond_access_host (gaa_ptr gaa,
gaa_sc_ptr sc,
gaa_condition *cond,
gaa_time_period *valid_time,
gaa_request_right_ptr right,
gaa_status rstatus,
gaa_string_data estatus,
gaa_status *output_flags,
void *params)
{
cb_params *cbp;
request_rec *r;
host_parser p;
host_answer answer;
gaa_status ret=GAA_S_SUCCESS;
int gothost = 0;
unsigned long remoteip;
char *remotehost;
cbp=(cb_params *)params;
if (cbp==0)
{
return GAA_S_INVALID_ARG;
}
r=cbp->record;
remoteip= r->connection->remote_addr.sin_addr.s_addr;
remotehost = ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_DOUBLE_REV);
LOG3("Evaluating IP:%X, HOST: %s",remoteip,remotehost);
if (strcasecmp(cond->authority,"local")!=0)
{
LOG1("Unevaluated Condition: Unknown authority for access host condition");
return GAA_S_SUCCESS;
}
host_initparser(&p, cond->value);
answer=host_do_parse(&p, remoteip, remotehost);
*output_flags |= GAA_COND_FLG_EVALUATED;
if (answer==HA_ERROR)
{
LOG2("Host Parser Report Error: %s",p.error);
ret=GAA_S_POLICY_PARSING_FAILURE;
}
else if (answer==HA_ALLOW)
{
*output_flags |= GAA_COND_FLG_MET;
}
else //TA_DENY
{
//none
}
host_freeparser(&p);
return ret;
}
gaa_status cb_check_cond_access_time (gaa_ptr gaa,
gaa_sc_ptr sc,
gaa_condition *cond,
gaa_time_period *valid_time,
gaa_request_right_ptr right,
gaa_status rstatus,
gaa_string_data estatus,
gaa_status *output_flags,
void *params)
{
cb_params *cbp;
request_rec *r;
time_parser p;
time_answer answer;
time_t timet;
struct tm *timem;
gaa_status ret=GAA_S_SUCCESS;
cbp=(cb_params *)params;
if (cbp==0)
{
return GAA_S_INVALID_ARG;
}
r=cbp->record;
if (strcasecmp(cond->authority,"GMT")==0)
{
time(&timet);
timem=gmtime(&timet);
}
else if (strcasecmp(cond->authority,"LOCAL")==0)
{
time(&timet);
timem=gmtime(&timet);
}
else
{
LOG1("Unevaluated Condition: Unknown authority for access time condition");
return GAA_S_SUCCESS;
}
*output_flags |= GAA_COND_FLG_EVALUATED;
time_initparser(&p, cond->value);
answer=time_do_parse(&p, timem);
if (answer==TA_ERROR)
{
LOG2("Time Parser Report Error: %s",p.error);
ret=GAA_S_POLICY_PARSING_FAILURE;
}
else if (answer==TA_ALLOW)
{
*output_flags |= GAA_COND_FLG_MET;
}
else //TA_DENY
{
//none
}
time_freeparser(&p);
return ret;
}
*/
id_answer apache_eval_id(int type, char* method, char* conf, void* auth_info)
{
id_answer answer=IA_FAIL;
request_rec *r=(request_rec *)auth_info;
array_header *reqs_arr;
require_line *reqs;
char *tmp_requirement;
int access_status;
int m;
int x;
if (strcasecmp(method,"apache")==0)
{
m=r->method_number;
reqs_arr=ap_requires(r);
if (reqs_arr == NULL)
{
return;
}
reqs = (require_line *) reqs_arr->elts;
for (x = 0; x < reqs_arr->nelts; x++)
{
if (! (reqs[x].method_mask & (1 << m)))
{
continue;
}
access_status = ap_check_user_id(r);
LOG4("check_user_id for user \"%s\": [%d] %s",r->connection->user, access_status,
(access_status==AUTH_REQUIRED)?"AUTH_REQUIRED":(access_status==DECLINED)?"DECLINED":(access_status==OK)?"OK":"ERROR");
if (access_status == AUTH_REQUIRED)
{
answer=IA_DENY;
}
else if (access_status == DECLINED)
{
answer=IA_FAIL;
}
else if (access_status == OK)
{
tmp_requirement=reqs[x].requirement;
reqs[x].requirement=conf;
access_status=ap_check_auth(r);
LOG4("check_auth for policy \"%s\": [%d] %s",reqs[x].requirement, access_status,
(access_status==AUTH_REQUIRED)?"AUTH_REQUIRED":(access_status==DECLINED)?"DECLINED":(access_status==OK)?"OK":"ERROR");
answer= (access_status == AUTH_REQUIRED) ? IA_DENY :
(access_status == DECLINED) ? IA_FAIL :
(access_status == OK) ? IA_ALLOW : IA_FAIL ;
reqs[x].requirement=tmp_requirement;
LOG3("Apache Auth Evaluation Answer: [%d] %s",answer, (answer==IA_DENY)?"DENY":(answer==IA_FAIL)?"FAIL":(answer==IA_ALLOW)?"ALLOW":"ERROR");
}
else
{
answer=IA_FAIL;
}
break;
}
}
return answer;
}
gaa_status cb_check_cond_access_id (gaa_ptr gaa,
gaa_sc_ptr sc,
gaa_condition *cond,
gaa_time_period *valid_time,
gaa_request_right_ptr right,
gaa_status rstatus,
gaa_string_data estatus,
gaa_status *output_flags,
void *params)
{
cb_params *cbp;
request_rec *r;
id_parser p;
id_answer answer;
gaa_status ret=GAA_S_SUCCESS;
cbp=(cb_params *)params;
if (cbp==0)
{
return GAA_S_INVALID_ARG;
}
r=cbp->record;
if (strcasecmp(cond->authority,"apache")==0)
{
*output_flags |= GAA_COND_FLG_EVALUATED;
id_initparser(&p,cond->value,apache_eval_id);
if (strcasecmp(cond->type,"cond_access_user")==0)
{
answer=id_do_parse_apache_user(&p, (void *)r);
}
else if (strcasecmp(cond->type, "cond_access_group")==0)
{
answer=id_do_parse_apache_group(&p, (void *)r);
}
else answer==IA_ERROR;
LOG3("GAA Evaluation Answer: [%d] %s",answer, (answer==IA_DENY)?"DENY":(answer==IA_FAIL)?"FAIL":(answer==IA_ALLOW)?"ALLOW":"ERROR");
if (answer==IA_ERROR)
{
LOG3("%s: Parser error: %s",cond->type, p.error);
ret=GAA_S_POLICY_PARSING_FAILURE;
}
else if (answer==IA_ALLOW)
{
*output_flags |= GAA_COND_FLG_MET;
}
else if (answer==IA_DENY)
{
LOG2("%s: Request for authentication",cond->type);
*output_flags &= (!GAA_COND_FLG_EVALUATED);
*output_flags |= GAA_COND_FLG_ENFORCE;
}
else //IA_FAIL
{
LOG2("%s, Unable to do authentication",cond->type);
}
id_freeparser(&p);
}
return ret;
}
gaa_status cb_check_cond_def_access_ctrl(gaa_ptr gaa,
gaa_sc_ptr sc,
gaa_condition *cond,
gaa_time_period *valid_time,
gaa_request_right_ptr right,
gaa_status rstatus,
gaa_string_data estatus,
gaa_status *output_flags,
void *params)
{
cb_params *cbp;
request_rec *r;
access_dir_conf *a;
int method;
int ret = OK;
cbp=(cb_params *)params;
if (cbp==0)
{
return GAA_S_INVALID_ARG;
}
r=cbp->record;
a=cbp->raw_policy;
/*
r=(request_rec *)params;
a=(access_dir_conf *)ap_get_module_config(r->per_dir_config, &access_module);
*/
if (r==0 || a==0)
{
return GAA_S_INVALID_ARG;
}
method = r->method_number;
if (a->order[method] == ALLOW_THEN_DENY) {
ret = FORBIDDEN;
if (find_allowdeny(r, a->allows, method))
ret = OK;
if (find_allowdeny(r, a->denys, method))
ret = FORBIDDEN;
}
else if (a->order[method] == DENY_THEN_ALLOW) {
if (find_allowdeny(r, a->denys, method))
ret = FORBIDDEN;
if (find_allowdeny(r, a->allows, method))
ret = OK;
}
else {
if (find_allowdeny(r, a->allows, method)
&& !find_allowdeny(r, a->denys, method))
ret = OK;
else
ret = FORBIDDEN;
}
if (ret == FORBIDDEN
&& (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r))) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"client denied by server configuration: %s",
r->filename);
}
*output_flags |= GAA_COND_FLG_EVALUATED;
if (ret==OK)
{
*output_flags |= GAA_COND_FLG_MET;
}
LOG2("The output flags of callback function [%d]",*output_flags);
return GAA_S_SUCCESS;
}
gaa_status cb_get_object_policy(gaa_ptr gaa, gaa_list_ptr *policy, gaa_string_data object, void *params)
{
request_rec *r=(request_rec *)params;
access_dir_conf *a=(access_dir_conf *)ap_get_module_config(r->per_dir_config, &access_module);
char *file=find_eaclfile(r,a->eacl,r->method_number);
int gaa_order=find_gaaorder(r,a->gaa,r->method_number);
int gaa_mode=find_gaamode(r,a->gaa,r->method_number);
gaa_status status;
if (!(*policy=gaa_new_policy_list()))
{
LOG2("Error: New policy list [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
if (gaa_order==EACL_THEN_APACHE)
{
if ((status=get_eacl_policy(r, gaa, policy, file))!=GAA_S_SUCCESS)
{
return status;
}
if ((status=get_def_policy(r, gaa, policy, gaa_mode))!=GAA_S_SUCCESS)
{
return status;
}
}
else //gaa_order==APACHE_THEN_EACL
{
if ((status=get_def_policy(r, gaa, policy, gaa_mode))!=GAA_S_SUCCESS)
{
return status;
}
if ((status=get_eacl_policy(r, gaa, policy, file))!=GAA_S_SUCCESS)
{
return status;
}
}
return GAA_S_SUCCESS;
}
static gaa_status gaa_check(request_rec *r)
{
gaa_status status;
gaa_status answer, answer1, answer2;
int gaa_mode;
gaa_sc_ptr sc=0;
gaa_ptr gaa=0;
FILE *cffile=0;
access_dir_conf *a=(access_dir_conf *)ap_get_module_config(r->per_dir_config, &access_module);
char *cfname;
r->connection->remote_host = ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_DOUBLE_REV);
LOG2("hostname: %s",r->connection->remote_host);
LOG1("Debug Gaa");
cfname="/zhou/apache_1.3.23/src/modules/standard/gaa.conf";
if ((cffile = fopen(cfname, "r")) == 0)
{
LOG2("gaa_init: couldn't open config file %s",cfname);
}
else
{
LOG1("file read, yes");
}
close (cffile);
if ((status=gaa_initialize(&gaa, (void *)cfname))!=GAA_S_SUCCESS)
{
LOG2("Error: Init Gaa [%d]",status);
return status;
}
if ((status=gaa_set_getpolicy_callback(gaa, cb_get_object_policy, r, 0))!=GAA_S_SUCCESS)
{
LOG2("Error: New getpolicy callback [%d]",status);
return status;
}
LOG1("Policy Callback set");
if (set_cond_callbacks(gaa, r)==-1)
{
return status;
}
LOG1("Condition Callback set");
/***************************
* Per Connection Setting *
***************************/
if ((status=gaa_new_sc(&sc))!=GAA_S_SUCCESS)
{
LOG2("Error: Gaa New Sc [%d]",status);
return status;
}
LOG1("Security Context Constructed");
answer=gaa_check_eacl(gaa,sc,r);
/*
gaa_mode=find_eaclmode(r,a->eacl,r->method_number);
LOG2("Gaa Mode: %d", gaa_mode);
if (gaa_mode==GAA_MODE_BOTH)
{
answer1=gaa_check_eacl(gaa,sc,r);
answer2=gaa_check_apache(gaa,sc,r);
if (answer1!=GAA_C_YES && answer1!=GAA_C_NO && answer1!=GAA_C_MAYBE)
{
answer=answer1; //error on eacl
}
else if (answer2!=GAA_C_YES && answer2!=GAA_C_NO && answer2!=GAA_C_MAYBE)
{
answer=answer2; //error on eacl
}
else if (answer1==GAA_C_YES && answer2==GAA_C_YES)
{
answer=GAA_C_YES; //both are YES
}
else
{
answer= (answer1==GAA_C_NO || answer2==GAA_C_NO) ? GAA_C_NO : GAA_C_MAYBE ;
}
}
else if (gaa_mode==GAA_MODE_EITHER)
{
if ((answer1=gaa_check_eacl(gaa,sc,r))==GAA_C_YES)
{
answer=GAA_C_YES;
}
else if ((answer2=gaa_check_apache(gaa,sc,r))==GAA_C_YES)
{
answer=GAA_C_YES;
}
else if (answer1==GAA_C_MAYBE || answer2==GAA_C_MAYBE)
{
answer=GAA_C_MAYBE;
}
else if (answer1==GAA_C_NO || answer2==GAA_C_NO)
{
answer=GAA_C_NO;
}
else
{
answer=answer1;
}
}
else if (gaa_mode==GAA_MODE_REPLACE)
{
answer=gaa_check_eacl(gaa,sc,r);
}
else if (gaa_mode==GAA_MODE_VALID)
{
answer1=gaa_check_eacl(gaa,sc,r);
if (answer1==GAA_C_YES || answer1==GAA_C_NO || answer1==GAA_C_MAYBE)
{
answer=answer1;
}
else
{
answer=gaa_check_apache(gaa,sc,r);
}
}
else
{
answer=GAA_MODE_INVALID;
}
*/
gaa_free_sc(sc);
gaa_free_gaa(gaa);
LOG1("Gaa finish");
return answer;
}
static gaa_status gaa_check_eacl(gaa_ptr gaa, gaa_sc_ptr sc, request_rec *r)
{
gaa_status status, authr_status;
gaa_list_ptr policy=0;
gaa_list_ptr request=0;
gaa_answer_ptr answer;
gaa_state_ptr state;
char outbuf[1000];
if ((status=gaa_get_object_policy_info("default", gaa, &policy))!=GAA_S_SUCCESS)
{
LOG2("Error: Get Policy Info [%d]",status);
return status;
}
LOG1("Policy Get");
if (!set_callback_params(&cbp, policy, r))
{
LOG1("Error: Raw policy not found");
}
if ((status=get_def_request(r,gaa,&request))!=GAA_S_SUCCESS)
{
return status;
}
LOG1("Request Constructed");
if ((status = gaa_new_answer(&answer)) != GAA_S_SUCCESS)
{
LOG2("Error: New answer [%d]",status);
return status;
}
if ((status = gaa_new_state(&state)) != GAA_S_SUCCESS)
{
LOG2("Error: New state [%d]",status);
return status;
}
gaadebug_sc_string(gaa,sc,outbuf,1000);
LOG1(outbuf);
authr_status = gaa_check_authorization(gaa,sc,policy,request,answer,state);
gaadebug_answer_string(gaa,outbuf,1000,answer);
LOG2("Answer: %s",outbuf);
LOG3("GAA EACL Authorization Result: [%d]%s",authr_status, (authr_status==GAA_C_YES) ? "GAA_C_YES" :
(authr_status==GAA_C_NO) ? "GAA_C_NO" :
(authr_status==GAA_C_MAYBE) ? "GAA_C_MAYBE" : "GAA_ERROR") ;
return authr_status;
}
/*
static gaa_status gaa_check_apache(gaa_ptr gaa, gaa_sc_ptr sc, request_rec *r)
{
gaa_status status, authr_status;
gaa_list_ptr policy=0;
gaa_list_ptr request=0;
gaa_answer_ptr answer;
gaa_state_ptr state;
char outbuf[1000];
if ((status=gaa_get_object_policy_info("Apache", gaa, &policy))!=GAA_S_SUCCESS)
{
LOG2("Error: Get Policy Info [%d]",status);
return status;
}
LOG1("Policy Get");
if (!set_callback_params(&cbp, policy, r))
{
LOG1("Error: Raw policy not found");
}
if ((status=get_def_request(r,gaa,&request))!=GAA_S_SUCCESS)
{
return status;
}
LOG1("Request Constructed");
if ((status = gaa_new_answer(&answer)) != GAA_S_SUCCESS)
{
LOG2("Error: New answer [%d]",status);
return status;
}
if ((status = gaa_new_state(&state)) != GAA_S_SUCCESS)
{
LOG2("Error: New state [%d]",status);
return status;
}
gaadebug_sc_string(gaa,sc,outbuf,1000);
LOG1(outbuf);
authr_status = gaa_check_authorization(gaa,sc,policy,request,answer,state);
gaadebug_answer_string(gaa,outbuf,1000,answer);
LOG2("Answer: %s",outbuf);
LOG3("Apache Authorization Result: [%d]%s",authr_status,(authr_status==GAA_C_YES) ? "GAA_C_YES" :
(authr_status==GAA_C_NO) ? "GAA_C_NO" :
(authr_status==GAA_C_MAYBE) ? "GAA_C_MAYBE" : "GAA_ERROR" );
return authr_status;
}
*/
static int set_cond_callbacks(gaa_ptr *gaa, request_rec *r)
{
gaa_status status;
gaa_cond_eval_callback_ptr cond_def_cb;
gaa_cond_eval_callback_ptr cond_host_cb;
gaa_cond_eval_callback_ptr cond_time_cb;
gaa_cond_eval_callback_ptr cond_id_cb;
if ((status=gaa_new_cond_eval_callback(&cond_def_cb, cb_check_cond_def_access_ctrl,&cbp,0))!=GAA_S_SUCCESS)
{
LOG2("Error: New cond def callback [%d]",status);
return status;
}
if ((status=gaa_add_cond_eval_callback(gaa,cond_def_cb,"cond_def_access_ctrl","apache",0))!=GAA_S_SUCCESS)
{
LOG2("Error: Add cond def callback [%d]",status);
return status;
}
/*
if ((status=gaa_new_cond_eval_callback(&cond_host_cb, cb_check_cond_access_host,&cbp,0))!=GAA_S_SUCCESS)
{
LOG2("Error: New cond hsot callback [%d]",status);
return status;
}
if ((status=gaa_add_cond_eval_callback(gaa,cond_host_cb,"cond_access_host",0,0))!=GAA_S_SUCCESS)
{
LOG2("Error: Add cond host callback [%d]",status);
return status;
}
if ((status=gaa_new_cond_eval_callback(&cond_time_cb, cb_check_cond_access_time,&cbp,0))!=GAA_S_SUCCESS)
{
LOG2("Error: New cond time callback [%d]",status);
return status;
}
if ((status=gaa_add_cond_eval_callback(gaa,cond_time_cb,"cond_access_time",0,0))!=GAA_S_SUCCESS)
{
LOG2("Error: Add cond time callback [%d]",status);
return status;
}
*/
if ((status=gaa_new_cond_eval_callback(&cond_id_cb, cb_check_cond_access_id,&cbp,0))!=GAA_S_SUCCESS)
{
LOG2("Error: New cond user/group callback [%d]",status);
return status;
}
if ((status=gaa_add_cond_eval_callback(gaa,cond_id_cb,"cond_access_user",0,0))!=GAA_S_SUCCESS)
{
LOG2("Error: Add cond user callback [%d]",status);
return status;
}
if ((status=gaa_add_cond_eval_callback(gaa,cond_id_cb,"cond_access_group",0,0))!=GAA_S_SUCCESS)
{
LOG2("Error: Add cond group callback [%d]",status);
return status;
}
return GAA_S_SUCCESS;
}
static int set_callback_params(cb_params *cbp, gaa_list_ptr policy, request_rec *r)
{
gaaint_list_entry *ent;
gaa_policy_ptr policy_ptr;
cbp->record=r;
for (ent = gaa_list_first(policy); ent; ent = gaa_list_next(ent))
{
if (policy_ptr = (gaa_policy_ptr)gaa_list_entry_value(ent))
{
if (policy_ptr->raw_policy)
{
cbp->raw_policy=(access_dir_conf *)policy_ptr->raw_policy;
return 1;
}
}
}
return 0;
}
static gaa_status get_eacl_policy(request_rec* r, gaa_ptr gaa, gaa_list_ptr *policy, char *arg)
{
gaa_status status;
gaaint_list_entry *ent;
gaa_policy_ptr policy_ptr=0;
char *filename;
char outbuf[1000];
int fd;
char *temp;
if (r==0 || gaa==0)
{
LOG1("Error: gaa or request_rec null");
return (gaa_status)GAA_S_INTERNAL_ERR;
}
/*
if (*policy==0)
{
if (!(*policy=gaa_new_policy_list()))
{
LOG2("Error: New policy list [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
}
*/
if (arg==0)
{
LOG1("Error: no file specified");
return GAA_EACL_INVALID; //use apache default policy
}
if (arg[0]=='\0')
{
LOG1("Error: file empty");
return GAA_EACL_INVALID;
}
filename = (char *)malloc(sizeof(char) * PATHNAME_SIZE);
if(!filename) {
LOG1("Error: Allocate Mem for Pathname");
return (gaa_status)GAA_S_INTERNAL_ERR;
}
if (arg[0]=='/')
{
snprintf(filename,PATHNAME_SIZE,"%s",arg);
}
else
{
snprintf(filename,PATHNAME_SIZE,"%s",r->filename);
if ((temp=strrchr(filename,'/'))>=0)
{
snprintf(temp+sizeof(char),PATHNAME_SIZE-(temp-filename)/sizeof(char)-sizeof(char),"%s",arg);
}
else
{
snprintf(filename,PATHNAME_SIZE,"%s",arg);
}
}
if (!(fd=open(filename,O_RDONLY)))
{
LOG1("File not found");
free(filename);
return GAA_EACL_INVALID;
}
close(fd);
LOG2("Eacl file: %s", filename);
status=gaasimple_read_local_eacl(gaa, policy, (void *)&filename);
free(filename);
/*
for (ent = gaa_list_first(*policy); ent; ent = gaa_list_next(ent))
{
if (policy_ptr = (gaa_policy_ptr *)gaa_list_entry_value(ent))
{
gaadebug_policy_string(gaa,outbuf,1000,policy_ptr);
}
}
*/
return GAA_S_SUCCESS;
}
static gaa_status get_def_policy(request_rec* r, gaa_ptr gaa, gaa_list_ptr *policy, int mode)
{
gaa_status status;
gaa_policy_ptr policy_ptr=0;
gaa_policy_right_ptr right=0;
gaa_condition_ptr cond=0;
char outbuf[1000];
/*
if (*policy==0)
{
if (!(*policy=gaa_new_policy_list()))
{
LOG2("Error: New policy list [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
}
*/
if ((status=gaa_new_policy(&policy_ptr))!=GAA_S_SUCCESS)
{
LOG2("Error: New policy [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
policy_ptr->raw_policy=(void *)(access_dir_conf *)ap_get_module_config(r->per_dir_config, &access_module);
if ((status=gaa_new_policy_right(gaa,&right,pos_access_right,"LIMIT","ALL"))!=GAA_S_SUCCESS)
{
LOG2("Error: New policy right [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
if ((status=gaa_new_condition(&cond,"cond_def_access_ctrl","apache",""))!=GAA_S_SUCCESS)
{
LOG2("Error: New condition [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
if ((status=gaa_add_condition(right,cond,pre_cond))!=GAA_S_SUCCESS)
{
LOG2("Error: Add condition [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
if ((status=gaa_add_policy_entry(policy_ptr,right,1,1,undefined))!=GAA_S_SUCCESS)
{
LOG2("Error: Add policy entry [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
if (mode==MODE_EXPAND)
{
policy_ptr->mode=expand;
}
else if (mode==MODE_NARROW)
{
policy_ptr->mode=narrow;
}
else if (mode==MODE_EXACT)
{
policy_ptr->mode=exact;
}
if ((status=gaa_add_policy(*policy,policy_ptr))!=GAA_S_SUCCESS)
{
LOG2("Error: Add policy [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
/*
gaadebug_policy_string(gaa,outbuf,1000,policy_ptr);
LOG1(outbuf);
*/
return GAA_S_SUCCESS;
}
typedef struct
{
unsigned long ip;
char *hostname;
} host_info;
typedef struct
{
char *method;
} method_info;
static gaa_status get_def_request(request_rec *r, gaa_ptr gaa, gaa_list_ptr *request)
{
gaa_status status;
gaa_request_right_ptr rright;
char outbuf[1000];
host_info hi;
method_info mi;
if ((*request=gaa_new_req_rightlist(1))==0) //1=all right contain will be freed
{
LOG1("Error: New request list");
return (gaa_status)GAA_S_INTERNAL_ERR;
}
if ((status=gaa_new_request_right(gaa,&rright,"LIMIT","ALL"))!=GAA_S_SUCCESS) //1=all right contain will be freed
{
LOG2("Error: New request right [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
if ((status=gaa_add_request_right(*request,rright))!=GAA_S_SUCCESS) //1=all right contain will be freed
{
LOG2("Error: Add request right [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
if ((status=gaa_add_option(rright,"request_rec","apache",(void *)r, 0))!=GAA_S_SUCCESS)
{
LOG2("Error: Add option [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
hi.ip=r->connection->remote_addr.sin_addr.s_addr;
hi.hostname=r->connection->remote_host;
LOG3("ip: %x, hostname: %s",hi.ip, hi.hostname);
if ((status=gaa_add_option(rright,"host","apache",(void *)&hi, 0))!=GAA_S_SUCCESS)
{
LOG2("Error: Add option [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
mi.method=r->method;
LOG2("methodname: %s",mi.method);
if ((status=gaa_add_option(rright,"method","apache",(void *)&mi, 0))!=GAA_S_SUCCESS)
{
LOG2("Error: Add option [%d]",status);
return (gaa_status)GAA_S_INTERNAL_ERR;
}
gaadebug_request_right_string(gaa,outbuf,1000,rright);
LOG1(outbuf);
return GAA_S_SUCCESS;
}
static int check_dir_access(request_rec *r)
{
/*
*original apache check_dir_access codes
*
int method = r->method_number;
access_dir_conf *a =
(access_dir_conf *)
ap_get_module_config(r->per_dir_config, &access_module);
int ret = OK;
if (a->order[method] == ALLOW_THEN_DENY) {
ret = FORBIDDEN;
if (find_allowdeny(r, a->allows, method))
ret = OK;
if (find_allowdeny(r, a->denys, method))
ret = FORBIDDEN;
}
else if (a->order[method] == DENY_THEN_ALLOW) {
if (find_allowdeny(r, a->denys, method))
ret = FORBIDDEN;
if (find_allowdeny(r, a->allows, method))
ret = OK;
}
else {
if (find_allowdeny(r, a->allows, method)
&& !find_allowdeny(r, a->denys, method))
ret = OK;
else
ret = FORBIDDEN;
}
if (ret == FORBIDDEN
&& (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r))) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"client denied by server configuration: %s",
r->filename);
}
return ret;
*/
gaa_status answer;
int ret=FORBIDDEN;
answer=gaa_check(r);
if (answer==GAA_C_YES)
{
ret=OK;
}
else if (answer==GAA_C_NO)
{
ret=FORBIDDEN;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"client denied by server configuration: %s",
r->filename);
}
else if (answer==GAA_C_MAYBE)
{
ret=AUTH_REQUIRED;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"authentication is required: %s",
r->filename);
}
else
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"encounter internal error using gaa-api, file: %s answer: %d",
r->filename, answer);
}
return ret;
}
module MODULE_VAR_EXPORT access_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_access_dir_config, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
access_cmds,
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
check_dir_access, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
NULL /* post read-request */
};