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