1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * Security options etc. 19 * 20 * Module derived from code originally written by Rob McCool 21 * 22 */ 23 24#include "apr_strings.h" 25#include "apr_network_io.h" 26#include "apr_md5.h" 27 28#define APR_WANT_STRFUNC 29#define APR_WANT_BYTEFUNC 30#include "apr_want.h" 31 32#include "ap_config.h" 33#include "httpd.h" 34#include "http_core.h" 35#include "http_config.h" 36#include "http_log.h" 37#include "http_request.h" 38 39#if APR_HAVE_NETINET_IN_H 40#include <netinet/in.h> 41#endif 42 43enum allowdeny_type { 44 T_ENV, 45 T_NENV, 46 T_ALL, 47 T_IP, 48 T_HOST, 49 T_FAIL 50}; 51 52typedef struct { 53 apr_int64_t limited; 54 union { 55 char *from; 56 apr_ipsubnet_t *ip; 57 } x; 58 enum allowdeny_type type; 59} allowdeny; 60 61/* things in the 'order' array */ 62#define DENY_THEN_ALLOW 0 63#define ALLOW_THEN_DENY 1 64#define MUTUAL_FAILURE 2 65 66typedef struct { 67 int order[METHODS]; 68 apr_array_header_t *allows; 69 apr_array_header_t *denys; 70} authz_host_dir_conf; 71 72module AP_MODULE_DECLARE_DATA authz_host_module; 73 74static void *create_authz_host_dir_config(apr_pool_t *p, char *dummy) 75{ 76 int i; 77 authz_host_dir_conf *conf = 78 (authz_host_dir_conf *)apr_pcalloc(p, sizeof(authz_host_dir_conf)); 79 80 for (i = 0; i < METHODS; ++i) { 81 conf->order[i] = DENY_THEN_ALLOW; 82 } 83 conf->allows = apr_array_make(p, 1, sizeof(allowdeny)); 84 conf->denys = apr_array_make(p, 1, sizeof(allowdeny)); 85 86 return (void *)conf; 87} 88 89static const char *order(cmd_parms *cmd, void *dv, const char *arg) 90{ 91 authz_host_dir_conf *d = (authz_host_dir_conf *) dv; 92 int i, o; 93 94 if (!strcasecmp(arg, "allow,deny")) 95 o = ALLOW_THEN_DENY; 96 else if (!strcasecmp(arg, "deny,allow")) 97 o = DENY_THEN_ALLOW; 98 else if (!strcasecmp(arg, "mutual-failure")) 99 o = MUTUAL_FAILURE; 100 else 101 return "unknown order"; 102 103 for (i = 0; i < METHODS; ++i) 104 if (cmd->limited & (AP_METHOD_BIT << i)) 105 d->order[i] = o; 106 107 return NULL; 108} 109 110static const char *allow_cmd(cmd_parms *cmd, void *dv, const char *from, 111 const char *where_c) 112{ 113 authz_host_dir_conf *d = (authz_host_dir_conf *) dv; 114 allowdeny *a; 115 char *where = apr_pstrdup(cmd->pool, where_c); 116 char *s; 117 char msgbuf[120]; 118 apr_status_t rv; 119 120 if (strcasecmp(from, "from")) 121 return "allow and deny must be followed by 'from'"; 122 123 a = (allowdeny *) apr_array_push(cmd->info ? d->allows : d->denys); 124 a->x.from = where; 125 a->limited = cmd->limited; 126 127 if (!strncasecmp(where, "env=!", 5)) { 128 a->type = T_NENV; 129 a->x.from += 5; 130 131 } 132 else if (!strncasecmp(where, "env=", 4)) { 133 a->type = T_ENV; 134 a->x.from += 4; 135 136 } 137 else if (!strcasecmp(where, "all")) { 138 a->type = T_ALL; 139 } 140 else if ((s = ap_strchr(where, '/'))) { 141 *s++ = '\0'; 142 rv = apr_ipsubnet_create(&a->x.ip, where, s, cmd->pool); 143 if(APR_STATUS_IS_EINVAL(rv)) { 144 /* looked nothing like an IP address */ 145 return "An IP address was expected"; 146 } 147 else if (rv != APR_SUCCESS) { 148 apr_strerror(rv, msgbuf, sizeof msgbuf); 149 return apr_pstrdup(cmd->pool, msgbuf); 150 } 151 a->type = T_IP; 152 } 153 else if (!APR_STATUS_IS_EINVAL(rv = apr_ipsubnet_create(&a->x.ip, where, 154 NULL, cmd->pool))) { 155 if (rv != APR_SUCCESS) { 156 apr_strerror(rv, msgbuf, sizeof msgbuf); 157 return apr_pstrdup(cmd->pool, msgbuf); 158 } 159 a->type = T_IP; 160 } 161 else { /* no slash, didn't look like an IP address => must be a host */ 162 a->type = T_HOST; 163 } 164 165 return NULL; 166} 167 168static char its_an_allow; 169 170static const command_rec authz_host_cmds[] = 171{ 172 AP_INIT_TAKE1("order", order, NULL, OR_LIMIT, 173 "'allow,deny', 'deny,allow', or 'mutual-failure'"), 174 AP_INIT_ITERATE2("allow", allow_cmd, &its_an_allow, OR_LIMIT, 175 "'from' followed by hostnames or IP-address wildcards"), 176 AP_INIT_ITERATE2("deny", allow_cmd, NULL, OR_LIMIT, 177 "'from' followed by hostnames or IP-address wildcards"), 178 {NULL} 179}; 180 181static int in_domain(const char *domain, const char *what) 182{ 183 int dl = strlen(domain); 184 int wl = strlen(what); 185 186 if ((wl - dl) >= 0) { 187 if (strcasecmp(domain, &what[wl - dl]) != 0) { 188 return 0; 189 } 190 191 /* Make sure we matched an *entire* subdomain --- if the user 192 * said 'allow from good.com', we don't want people from nogood.com 193 * to be able to get in. 194 */ 195 196 if (wl == dl) { 197 return 1; /* matched whole thing */ 198 } 199 else { 200 return (domain[0] == '.' || what[wl - dl - 1] == '.'); 201 } 202 } 203 else { 204 return 0; 205 } 206} 207 208static int find_allowdeny(request_rec *r, apr_array_header_t *a, int method) 209{ 210 211 allowdeny *ap = (allowdeny *) a->elts; 212 apr_int64_t mmask = (AP_METHOD_BIT << method); 213 int i; 214 int gothost = 0; 215 const char *remotehost = NULL; 216 217 for (i = 0; i < a->nelts; ++i) { 218 if (!(mmask & ap[i].limited)) { 219 continue; 220 } 221 222 switch (ap[i].type) { 223 case T_ENV: 224 if (apr_table_get(r->subprocess_env, ap[i].x.from)) { 225 return 1; 226 } 227 break; 228 229 case T_NENV: 230 if (!apr_table_get(r->subprocess_env, ap[i].x.from)) { 231 return 1; 232 } 233 break; 234 235 case T_ALL: 236 return 1; 237 238 case T_IP: 239 if (apr_ipsubnet_test(ap[i].x.ip, r->connection->remote_addr)) { 240 return 1; 241 } 242 break; 243 244 case T_HOST: 245 if (!gothost) { 246 int remotehost_is_ip; 247 248 remotehost = ap_get_remote_host(r->connection, 249 r->per_dir_config, 250 REMOTE_DOUBLE_REV, 251 &remotehost_is_ip); 252 253 if ((remotehost == NULL) || remotehost_is_ip) { 254 gothost = 1; 255 } 256 else { 257 gothost = 2; 258 } 259 } 260 261 if ((gothost == 2) && in_domain(ap[i].x.from, remotehost)) { 262 return 1; 263 } 264 break; 265 266 case T_FAIL: 267 /* do nothing? */ 268 break; 269 } 270 } 271 272 return 0; 273} 274 275static int check_dir_access(request_rec *r) 276{ 277 int method = r->method_number; 278 int ret = OK; 279 authz_host_dir_conf *a = (authz_host_dir_conf *) 280 ap_get_module_config(r->per_dir_config, &authz_host_module); 281 282 if (a->order[method] == ALLOW_THEN_DENY) { 283 ret = HTTP_FORBIDDEN; 284 if (find_allowdeny(r, a->allows, method)) { 285 ret = OK; 286 } 287 if (find_allowdeny(r, a->denys, method)) { 288 ret = HTTP_FORBIDDEN; 289 } 290 } 291 else if (a->order[method] == DENY_THEN_ALLOW) { 292 if (find_allowdeny(r, a->denys, method)) { 293 ret = HTTP_FORBIDDEN; 294 } 295 if (find_allowdeny(r, a->allows, method)) { 296 ret = OK; 297 } 298 } 299 else { 300 if (find_allowdeny(r, a->allows, method) 301 && !find_allowdeny(r, a->denys, method)) { 302 ret = OK; 303 } 304 else { 305 ret = HTTP_FORBIDDEN; 306 } 307 } 308 309 if (ret == HTTP_FORBIDDEN 310 && (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r))) { 311 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 312 "client denied by server configuration: %s%s", 313 r->filename ? "" : "uri ", 314 r->filename ? r->filename : r->uri); 315 } 316 317 return ret; 318} 319 320static void register_hooks(apr_pool_t *p) 321{ 322 /* This can be access checker since we don't require r->user to be set. */ 323 ap_hook_access_checker(check_dir_access,NULL,NULL,APR_HOOK_MIDDLE); 324} 325 326module AP_MODULE_DECLARE_DATA authz_host_module = 327{ 328 STANDARD20_MODULE_STUFF, 329 create_authz_host_dir_config, /* dir config creater */ 330 NULL, /* dir merger --- default is to override */ 331 NULL, /* server config */ 332 NULL, /* merge server config */ 333 authz_host_cmds, 334 register_hooks /* register hooks */ 335}; 336