/* ==================================================================== * 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 */ };