1 #include "first.h" 2 3 #include "base.h" 4 #include "array.h" 5 #include "buffer.h" 6 #include "log.h" 7 8 #include "plugin.h" 9 10 #include <stdlib.h> 11 #include <string.h> 12 13 typedef struct { 14 const array *access_allow; 15 const array *access_deny; 16 } plugin_config; 17 18 typedef struct { 19 PLUGIN_DATA; 20 plugin_config defaults; 21 plugin_config conf; 22 } plugin_data; 23 24 INIT_FUNC(mod_access_init) { 25 return calloc(1, sizeof(plugin_data)); 26 } 27 28 static void mod_access_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 29 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 30 case 0: /* url.access-deny */ 31 pconf->access_deny = cpv->v.a; 32 break; 33 case 1: /* url.access-allow */ 34 pconf->access_allow = cpv->v.a; 35 break; 36 default:/* should not happen */ 37 return; 38 } 39 } 40 41 static void mod_access_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 42 do { 43 mod_access_merge_config_cpv(pconf, cpv); 44 } while ((++cpv)->k_id != -1); 45 } 46 47 static void mod_access_patch_config(request_st * const r, plugin_data * const p) { 48 p->conf = p->defaults; /* copy small struct instead of memcpy() */ 49 /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/ 50 for (int i = 1, used = p->nconfig; i < used; ++i) { 51 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 52 mod_access_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 53 } 54 } 55 56 SETDEFAULTS_FUNC(mod_access_set_defaults) { 57 static const config_plugin_keys_t cpk[] = { 58 { CONST_STR_LEN("url.access-deny"), 59 T_CONFIG_ARRAY_VLIST, 60 T_CONFIG_SCOPE_CONNECTION } 61 ,{ CONST_STR_LEN("url.access-allow"), 62 T_CONFIG_ARRAY_VLIST, 63 T_CONFIG_SCOPE_CONNECTION } 64 ,{ NULL, 0, 65 T_CONFIG_UNSET, 66 T_CONFIG_SCOPE_UNSET } 67 }; 68 69 plugin_data * const p = p_d; 70 if (!config_plugin_values_init(srv, p, cpk, "mod_access")) 71 return HANDLER_ERROR; 72 73 /* initialize p->defaults from global config context */ 74 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 75 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 76 if (-1 != cpv->k_id) 77 mod_access_merge_config(&p->defaults, cpv); 78 } 79 80 return HANDLER_GO_ON; 81 } 82 83 __attribute_cold__ 84 static handler_t mod_access_reject (request_st * const r, plugin_data * const p) { 85 if (r->conf.log_request_handling) { 86 if (p->conf.access_allow && p->conf.access_allow->used) 87 log_error(r->conf.errh, __FILE__, __LINE__, 88 "url denied as failed to match any from access_allow %s", 89 r->uri.path.ptr); 90 else 91 log_error(r->conf.errh, __FILE__, __LINE__, 92 "url denied as we match access_deny %s", 93 r->uri.path.ptr); 94 } 95 96 r->http_status = 403; 97 r->handler_module = NULL; 98 return HANDLER_FINISHED; 99 } 100 101 static int mod_access_check (const array *allow, const array *deny, const buffer *urlpath, const int lc) { 102 103 if (allow && allow->used) { 104 const buffer *match = (!lc) 105 ? array_match_value_suffix(allow, urlpath) 106 : array_match_value_suffix_nc(allow, urlpath); 107 return (match != NULL); /* allowed if match; denied if none matched */ 108 } 109 110 if (deny && deny->used) { 111 const buffer *match = (!lc) 112 ? array_match_value_suffix(deny, urlpath) 113 : array_match_value_suffix_nc(deny, urlpath); 114 return (match == NULL); /* deny if match; allow if none matched */ 115 } 116 117 return 1; /* allowed (not denied) */ 118 } 119 120 /** 121 * URI handler 122 * 123 * we will get called twice: 124 * - after the clean up of the URL and 125 * - after the pathinfo checks are done 126 * 127 * this handles the issue of trailing slashes 128 */ 129 URIHANDLER_FUNC(mod_access_uri_handler) { 130 plugin_data *p = p_d; 131 132 mod_access_patch_config(r, p); 133 134 if (NULL == p->conf.access_allow && NULL == p->conf.access_deny) { 135 return HANDLER_GO_ON; /* access allowed; nothing to match */ 136 } 137 138 if (r->conf.log_request_handling) { 139 log_error(r->conf.errh, __FILE__, __LINE__, 140 "-- mod_access_uri_handler called"); 141 } 142 143 return mod_access_check(p->conf.access_allow, p->conf.access_deny, 144 &r->uri.path, r->conf.force_lowercase_filenames) 145 ? HANDLER_GO_ON /* access allowed */ 146 : mod_access_reject(r, p); /* access denied */ 147 } 148 149 150 int mod_access_plugin_init(plugin *p); 151 int mod_access_plugin_init(plugin *p) { 152 p->version = LIGHTTPD_VERSION_ID; 153 p->name = "access"; 154 155 p->init = mod_access_init; 156 p->set_defaults = mod_access_set_defaults; 157 p->handle_uri_clean = mod_access_uri_handler; 158 p->handle_subrequest_start = mod_access_uri_handler; 159 160 return 0; 161 } 162