1 #include "first.h" 2 3 #include "base.h" 4 #include "log.h" 5 #include "buffer.h" 6 7 #include "plugin.h" 8 9 #include <stdlib.h> 10 #include <string.h> 11 12 typedef struct { 13 array *access_allow; 14 array *access_deny; 15 } plugin_config; 16 17 typedef struct { 18 PLUGIN_DATA; 19 20 plugin_config **config_storage; 21 22 plugin_config conf; 23 } plugin_data; 24 25 INIT_FUNC(mod_access_init) { 26 plugin_data *p; 27 28 p = calloc(1, sizeof(*p)); 29 30 return p; 31 } 32 33 FREE_FUNC(mod_access_free) { 34 plugin_data *p = p_d; 35 36 UNUSED(srv); 37 38 if (!p) return HANDLER_GO_ON; 39 40 if (p->config_storage) { 41 size_t i; 42 for (i = 0; i < srv->config_context->used; i++) { 43 plugin_config *s = p->config_storage[i]; 44 45 if (NULL == s) continue; 46 47 array_free(s->access_allow); 48 array_free(s->access_deny); 49 50 free(s); 51 } 52 free(p->config_storage); 53 } 54 55 free(p); 56 57 return HANDLER_GO_ON; 58 } 59 60 SETDEFAULTS_FUNC(mod_access_set_defaults) { 61 plugin_data *p = p_d; 62 size_t i = 0; 63 64 config_values_t cv[] = { 65 { "url.access-deny", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, 66 { "url.access-allow", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, 67 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } 68 }; 69 70 p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *)); 71 72 for (i = 0; i < srv->config_context->used; i++) { 73 data_config const* config = (data_config const*)srv->config_context->data[i]; 74 plugin_config *s; 75 76 s = calloc(1, sizeof(plugin_config)); 77 s->access_deny = array_init(); 78 s->access_allow = array_init(); 79 80 cv[0].destination = s->access_deny; 81 cv[1].destination = s->access_allow; 82 83 p->config_storage[i] = s; 84 85 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { 86 return HANDLER_ERROR; 87 } 88 89 if (!array_is_vlist(s->access_deny)) { 90 log_error_write(srv, __FILE__, __LINE__, "s", 91 "unexpected value for url.access-deny; expected list of \"suffix\""); 92 return HANDLER_ERROR; 93 } 94 95 if (!array_is_vlist(s->access_allow)) { 96 log_error_write(srv, __FILE__, __LINE__, "s", 97 "unexpected value for url.access-allow; expected list of \"suffix\""); 98 return HANDLER_ERROR; 99 } 100 } 101 102 return HANDLER_GO_ON; 103 } 104 105 #define PATCH(x) \ 106 p->conf.x = s->x; 107 static int mod_access_patch_connection(server *srv, connection *con, plugin_data *p) { 108 size_t i, j; 109 plugin_config *s = p->config_storage[0]; 110 111 PATCH(access_allow); 112 PATCH(access_deny); 113 114 /* skip the first, the global context */ 115 for (i = 1; i < srv->config_context->used; i++) { 116 data_config *dc = (data_config *)srv->config_context->data[i]; 117 s = p->config_storage[i]; 118 119 /* condition didn't match */ 120 if (!config_check_cond(srv, con, dc)) continue; 121 122 /* merge config */ 123 for (j = 0; j < dc->value->used; j++) { 124 data_unset *du = dc->value->data[j]; 125 126 if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.access-deny"))) { 127 PATCH(access_deny); 128 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.access-allow"))) { 129 PATCH(access_allow); 130 } 131 } 132 } 133 134 return 0; 135 } 136 #undef PATCH 137 138 static int mod_access_check (const array *allow, const array *deny, const buffer *urlpath, const int lc) { 139 140 if (allow->used) { 141 const buffer *match = (!lc) 142 ? array_match_value_suffix(allow, urlpath) 143 : array_match_value_suffix_nc(allow, urlpath); 144 return (match != NULL); /* allowed if match; denied if none matched */ 145 } 146 147 if (deny->used) { 148 const buffer *match = (!lc) 149 ? array_match_value_suffix(deny, urlpath) 150 : array_match_value_suffix_nc(deny, urlpath); 151 return (match == NULL); /* deny if match; allow if none matched */ 152 } 153 154 return 1; /* allowed (not denied) */ 155 } 156 157 /** 158 * URI handler 159 * 160 * we will get called twice: 161 * - after the clean up of the URL and 162 * - after the pathinfo checks are done 163 * 164 * this handles the issue of trailing slashes 165 */ 166 URIHANDLER_FUNC(mod_access_uri_handler) { 167 plugin_data *p = p_d; 168 if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; 169 170 mod_access_patch_connection(srv, con, p); 171 172 if (0 == p->conf.access_allow->used && 0 == p->conf.access_deny->used) { 173 return HANDLER_GO_ON; /* access allowed; nothing to match */ 174 } 175 176 if (con->conf.log_request_handling) { 177 log_error_write(srv, __FILE__, __LINE__, "s", 178 "-- mod_access_uri_handler called"); 179 } 180 181 if (mod_access_check(p->conf.access_allow, p->conf.access_deny, 182 con->uri.path, con->conf.force_lowercase_filenames)) { 183 return HANDLER_GO_ON; /* access allowed */ 184 } 185 186 /* (else) access denied */ 187 if (con->conf.log_request_handling) { 188 if (p->conf.access_allow->used) { 189 log_error_write(srv, __FILE__, __LINE__, "sb", "url denied as failed to match any from access_allow", con->uri.path); 190 } 191 else { 192 log_error_write(srv, __FILE__, __LINE__, "sb", "url denied as we match access_deny", con->uri.path); 193 } 194 } 195 196 con->http_status = 403; 197 con->mode = DIRECT; 198 return HANDLER_FINISHED; 199 } 200 201 202 int mod_access_plugin_init(plugin *p); 203 int mod_access_plugin_init(plugin *p) { 204 p->version = LIGHTTPD_VERSION_ID; 205 p->name = buffer_init_string("access"); 206 207 p->init = mod_access_init; 208 p->set_defaults = mod_access_set_defaults; 209 p->handle_uri_clean = mod_access_uri_handler; 210 p->handle_subrequest_start = mod_access_uri_handler; 211 p->cleanup = mod_access_free; 212 213 p->data = NULL; 214 215 return 0; 216 } 217