/* Copyright 2002-2006 The Apache Software Foundation or its licensors, as * applicable. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This product includes software developed by * The Apache Software Foundation (http://www.apache.org/). * * Portions of this software were developed at the National Center * for Supercomputing Applications (NCSA) at the University of * Illinois at Urbana-Champaign. */ /* * Security options etc. * * Module derived from code originally written by Rob McCool * */ #include "apr_errno.h" #include "apr_file_io.h" #include "apr_general.h" #include "apr_mmap.h" #include "apr_network_io.h" #include "apr_strings.h" #include "apr_thread_mutex.h" #include "apr_thread_rwlock.h" #include "apr_time.h" #define APR_WANT_BYTEFUNC #define APR_WANT_STRFUNC #include "apr_want.h" #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_protocol.h" #include "http_request.h" #if !AP_MODULE_MAGIC_AT_LEAST(20050127, 0) #define AP_REG_EXTENDED REG_EXTENDED #define AP_REG_NOSUB REG_NOSUB typedef regex_t ap_regex_t; #endif #if !APR_HAS_THREADS typedef void apr_thread_mutex_t; typedef void apr_thread_rwlock_t; #define apr_thread_mutex_create(mutex, flags, pool) \ (*mutex = (apr_thread_mutex_t *)1, APR_SUCCESS) #define apr_thread_mutex_lock(mutex) (void)APR_SUCCESS #define apr_thread_mutex_unlock(mutex) (void)APR_SUCCESS #define apr_thread_rwlock_create(rwlock, pool) APR_SUCCESS #define apr_thread_rwlock_rdlock(rwlock) (void)APR_SUCCESS #define apr_thread_rwlock_wrlock(rwlock) (void)APR_SUCCESS #define apr_thread_rwlock_unlock(rwlock) (void)APR_SUCCESS #endif /* !APR_HAS_THREADS */ #if !APR_HAVE_IPV6 #define IN6_IS_ADDR_V4MAPPED(addr) FALSE typedef struct in6_addr in6_addr_t; #elif !defined __sun || !defined __SVR4 #define in6_addr_t struct in6_addr #endif struct apr_ipsubnet_t { /* not defined in apr_network_io.h ... */ int family; #if APR_HAVE_IPV6 apr_uint32_t sub[4]; /* big enough for IPv4 and IPv6 addresses */ apr_uint32_t mask[4]; #else apr_uint32_t sub[1]; apr_uint32_t mask[1]; #endif }; typedef enum { LIST_TYPE_BLACK, LIST_TYPE_WHITE, LIST_TYPE_UNKNOWN } list_type_t; typedef struct { apr_int64_t limited; list_type_t type; char *file; } authz_iplist_dir_conf; typedef struct { int term; apr_array_header_t *list; apr_hash_t *subs; } iplist_impl_t; typedef struct { apr_thread_rwlock_t *rwlock; volatile apr_time_t mtime; apr_pool_t *pool; iplist_impl_t *ips, *hosts; } iplist_t; module AP_MODULE_DECLARE_DATA authz_iplist_module; #define HANDLER_NAME "authz-iplist" static const char REGEX_SPECIALS[] = ".\\[]()*+?{}^$|"; #define IS_REGEX_SPECIAL(c) (ap_strchr_c(REGEX_SPECIALS, c) != NULL) static apr_thread_mutex_t *iplists_mutex; static apr_hash_t *iplists; static void *create_authz_iplist_dir_config(apr_pool_t *p, char *dummy) { authz_iplist_dir_conf *conf = apr_pcalloc(p, sizeof *conf); return conf; } static const char *config_iplist(cmd_parms *cmd, void *dv, const char *type, const char *file) { authz_iplist_dir_conf *d = dv; d->limited = cmd->limited; d->type = !strcasecmp(type, "Black") ? LIST_TYPE_BLACK : !strcasecmp(type, "White") ? LIST_TYPE_WHITE : LIST_TYPE_UNKNOWN; if (d->type == LIST_TYPE_UNKNOWN) return "unknown list type"; if (!(d->file = ap_server_root_relative(cmd->pool, file))) return "bad file path"; return NULL; } static const command_rec authz_iplist_cmds[] = { AP_INIT_TAKE2("AuthzIPList", config_iplist, NULL, OR_LIMIT, "args: type file\n" "(type: \"Black\" or \"White\")\n" "(file: name of the file containing IP addrs / subnets / hosts)"), {NULL} }; static apr_status_t regist_ip(iplist_impl_t *list, const apr_ipsubnet_t *ip, apr_size_t i, apr_size_t n, apr_pool_t *p) { if (i + i >= n) list->term = ip->family; else if (((apr_uint16_t *)ip->mask)[i] == 0xFFFF) { iplist_impl_t *sub; if (!list->subs) list->subs = apr_hash_make(p); if (!(sub = apr_hash_get(list->subs, (apr_uint16_t *)ip->sub + i, 2))) { sub = apr_pcalloc(p, sizeof *sub); apr_hash_set(list->subs, (apr_uint16_t *)ip->sub + i, 2, sub); } return regist_ip(sub, ip, ++i, n, p); } else { if (!list->list) list->list = apr_array_make(p, 4, sizeof ip); *(const apr_ipsubnet_t **)apr_array_push(list->list) = ip; } return APR_SUCCESS; } static int is_hit_ip(const iplist_impl_t *list, apr_sockaddr_t *sa, apr_size_t i, apr_size_t n) { iplist_impl_t *sub; if (list->term == (sa->family != APR_INET && IN6_IS_ADDR_V4MAPPED((in6_addr_t *)sa->ipaddr_ptr) ? APR_INET : sa->family)) return TRUE; if (list->list) { int j; for (j = 0; j < list->list->nelts; j++) if (apr_ipsubnet_test(((apr_ipsubnet_t **)list->list->elts)[j], sa)) return TRUE; } return i + i < n && list->subs && (sub = apr_hash_get(list->subs, (apr_uint16_t *)sa->ipaddr_ptr + (sa->family != APR_INET && IN6_IS_ADDR_V4MAPPED((in6_addr_t *)sa->ipaddr_ptr) ? 3 * 2 : 0) + i, 2)) ? is_hit_ip(sub, sa, ++i, n) : FALSE; } static apr_status_t regist_host(iplist_impl_t *list, const apr_byte_t *host, const apr_byte_t *ptr, apr_pool_t *p) { const apr_byte_t *dom; for (dom = ptr; --dom > host && !IS_REGEX_SPECIAL(*dom); ) ; if (host == ptr) list->term = TRUE; else if (*dom++ == '.' || (--dom == host && !IS_REGEX_SPECIAL(*dom))) { iplist_impl_t *sub; if (!list->subs) list->subs = apr_hash_make(p); if (!(sub = apr_hash_get(list->subs, dom, ptr - dom))) { sub = apr_pcalloc(p, sizeof *sub); apr_hash_set(list->subs, apr_pstrmemdup(p, dom, ptr - dom), ptr - dom, sub); } return regist_host(sub, host, dom - (dom == host ? 0 : dom - 2 >= host && dom[-2] == '\\' ? 2 : 1), p); } else { ap_regex_t *regex; if (!(regex = ap_pregcomp(p, host, AP_REG_EXTENDED | AP_REG_NOSUB))) return APR_EINVAL; if (!list->list) list->list = apr_array_make(p, 4, sizeof regex); *(ap_regex_t **)apr_array_push(list->list) = regex; } return APR_SUCCESS; } static int is_hit_host(const iplist_impl_t *list, const apr_byte_t *host, const apr_byte_t *ptr) { const apr_byte_t *dom; iplist_impl_t *sub; if (list->term) return TRUE; if (list->list) { int i; for (i = 0; i < list->list->nelts; i++) if (!ap_regexec(((ap_regex_t **)list->list->elts)[i], host, 0, NULL, 0)) return TRUE; } for (dom = ptr; --dom > host && *dom != '.'; ) ; return host < ptr && (*dom++ == '.' || --dom) && list->subs && (sub = apr_hash_get(list->subs, dom, ptr - dom)) ? is_hit_host(sub, host, dom == host ? dom : --dom) : FALSE; } static apr_status_t create_list(iplist_t **list, const char *file, apr_pool_t *p) { apr_status_t rv; *list = apr_palloc(p, sizeof **list); (*list)->mtime = 0; if ((rv = apr_thread_rwlock_create(&(*list)->rwlock, p)) || (rv = apr_pool_create(&(*list)->pool, p))) return rv; apr_hash_set(iplists, apr_pstrdup(p, file), APR_HASH_KEY_STRING, *list); return APR_SUCCESS; } static apr_status_t update_list(iplist_t *list, const char *file, request_rec *r) { apr_status_t rv; apr_byte_t *s, *eol; apr_file_t *f; apr_finfo_t finfo; apr_mmap_t *m; apr_pool_clear(list->pool); /* these resources will be deallocated when r->pool is destroyed */ if ((rv = apr_file_open(&f, file, APR_READ, APR_OS_DEFAULT, r->pool)) || (rv = apr_file_lock(f, APR_FLOCK_SHARED)) || (rv = apr_file_info_get(&finfo, APR_FINFO_MIN, f)) || (finfo.size && (rv = apr_mmap_create(&m, f, 0, finfo.size, APR_MMAP_READ, r->pool)))) { list->mtime = 0; return rv; } list->mtime = finfo.mtime; list->ips = apr_pcalloc(list->pool, sizeof *list->ips); list->hosts = apr_pcalloc(list->pool, sizeof *list->hosts); if (!finfo.size) return APR_SUCCESS; for (s = m->mm; s < (apr_byte_t *)m->mm + m->size; s = eol + 1) { apr_byte_t *ipstr, *substr; apr_ipsubnet_t *subnet; for (eol = s, rv = APR_SUCCESS; eol < (apr_byte_t *)m->mm + m->size && *eol != '\n'; eol++) if (!rv && *eol != '.' && IS_REGEX_SPECIAL(*eol)) rv = APR_EINVAL; if (s == eol || *s == '#') continue; ipstr = apr_pstrmemdup(r->pool, s, eol - s); substr = ap_strchr(ipstr, '/'); if (substr) *substr++ = '\0'; if (!rv && !(rv = apr_ipsubnet_create(&subnet, ipstr, substr, list->pool))) regist_ip(list->ips, subnet, 0, subnet->family == APR_INET ? 4 : 16/*IPv6*/, list->pool); else if (APR_STATUS_IS_EINVAL(rv)) { if (substr) *--substr = '/'; ap_str_tolower(ipstr); regist_host(list->hosts, ipstr, ipstr + strlen(ipstr), list->pool); } else if (!APR_STATUS_IS_EBADIP(rv)) { list->mtime = 0; apr_pool_clear(list->pool); return rv; } } return APR_SUCCESS; } static void display_ips(iplist_impl_t *list, apr_size_t n, request_rec *r) { apr_hash_index_t *hi; ap_rprintf(r, "%*s
Last-Mod: %s
\n