xref: /lighttpd1.4/src/mod_alias.c (revision 34709192)
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 *alias;
15 } plugin_config;
16 
17 typedef struct {
18     PLUGIN_DATA;
19     plugin_config defaults;
20     plugin_config conf;
21 } plugin_data;
22 
23 INIT_FUNC(mod_alias_init) {
24     return calloc(1, sizeof(plugin_data));
25 }
26 
27 static void mod_alias_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
28     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
29       case 0: /* alias.url */
30         pconf->alias = cpv->v.a;
31         break;
32       default:/* should not happen */
33         return;
34     }
35 }
36 
37 static void mod_alias_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
38     do {
39         mod_alias_merge_config_cpv(pconf, cpv);
40     } while ((++cpv)->k_id != -1);
41 }
42 
43 static void mod_alias_patch_config(request_st * const r, plugin_data * const p) {
44     p->conf = p->defaults; /* copy small struct instead of memcpy() */
45     /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/
46     for (int i = 1, used = p->nconfig; i < used; ++i) {
47         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
48             mod_alias_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
49     }
50 }
51 
52 static int mod_alias_check_order(server * const srv, const array * const a) {
53     for (uint32_t j = 0; j < a->used; ++j) {
54         const buffer * const prefix = &a->sorted[j]->key;
55         const size_t plen = buffer_string_length(prefix);
56         for (uint32_t k = j + 1; k < a->used; ++k) {
57             const buffer * const key = &a->sorted[k]->key;
58             if (buffer_string_length(key) < plen) {
59                 break;
60             }
61             if (memcmp(key->ptr, prefix->ptr, plen) != 0) {
62                 break;
63             }
64             /* ok, they have same prefix. check position */
65             const data_unset *dj = a->sorted[j];
66             const data_unset *dk = a->sorted[k];
67             const data_unset **data = (const data_unset **)a->data;
68             while (*data != dj && *data != dk) ++data;
69             if (*data == dj) {
70                 log_error(srv->errh, __FILE__, __LINE__,
71                   "url.alias: `%s' will never match as `%s' matched first",
72                   key->ptr, prefix->ptr);
73                 return 0;
74             }
75         }
76     }
77     return 1;
78 }
79 
80 SETDEFAULTS_FUNC(mod_alias_set_defaults) {
81     static const config_plugin_keys_t cpk[] = {
82       { CONST_STR_LEN("alias.url"),
83         T_CONFIG_ARRAY_KVSTRING,
84         T_CONFIG_SCOPE_CONNECTION }
85      ,{ NULL, 0,
86         T_CONFIG_UNSET,
87         T_CONFIG_SCOPE_UNSET }
88     };
89 
90     plugin_data * const p = p_d;
91     if (!config_plugin_values_init(srv, p, cpk, "mod_alias"))
92         return HANDLER_ERROR;
93 
94     /* process and validate config directives
95      * (init i to 0 if global context; to 1 to skip empty global context) */
96     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
97         const config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
98         for (; -1 != cpv->k_id; ++cpv) {
99             switch (cpv->k_id) {
100               case 0: /* alias.url */
101                 if (cpv->v.a->used >= 2 && !mod_alias_check_order(srv,cpv->v.a))
102                     return HANDLER_ERROR;
103                 break;
104               default:/* should not happen */
105                 break;
106             }
107         }
108     }
109 
110     /* initialize p->defaults from global config context */
111     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
112         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
113         if (-1 != cpv->k_id)
114             mod_alias_merge_config(&p->defaults, cpv);
115     }
116 
117     return HANDLER_GO_ON;
118 }
119 
120 PHYSICALPATH_FUNC(mod_alias_physical_handler) {
121 	plugin_data *p = p_d;
122 	char *uri_ptr;
123 	size_t uri_len = buffer_string_length(&r->physical.path);
124 	size_t basedir_len, alias_len;
125 	data_string *ds;
126 
127 	if (0 == uri_len) return HANDLER_GO_ON;
128 
129 	mod_alias_patch_config(r, p);
130 	if (NULL == p->conf.alias) return HANDLER_GO_ON;
131 
132 	/* do not include trailing slash on basedir */
133 	basedir_len = buffer_string_length(&r->physical.basedir);
134 	if ('/' == r->physical.basedir.ptr[basedir_len-1]) --basedir_len;
135 	uri_len -= basedir_len;
136 	uri_ptr = r->physical.path.ptr + basedir_len;
137 
138 	ds = (!r->conf.force_lowercase_filenames)
139 	   ? (data_string *)array_match_key_prefix_klen(p->conf.alias, uri_ptr, uri_len)
140 	   : (data_string *)array_match_key_prefix_nc_klen(p->conf.alias, uri_ptr, uri_len);
141 	if (NULL == ds) { return HANDLER_GO_ON; }
142 
143 			/* matched */
144 
145 			/* check for path traversal in url-path following alias if key
146 			 * does not end in slash, but replacement value ends in slash */
147 			alias_len = buffer_string_length(&ds->key);
148 			if (uri_ptr[alias_len] == '.') {
149 				char *s = uri_ptr + alias_len + 1;
150 				if (*s == '.') ++s;
151 				if (*s == '/' || *s == '\0') {
152 					size_t vlen = buffer_string_length(&ds->value);
153 					if (0 != alias_len && ds->key.ptr[alias_len-1] != '/'
154 					    && 0 != vlen && ds->value.ptr[vlen-1] == '/') {
155 						r->http_status = 403;
156 						return HANDLER_FINISHED;
157 					}
158 				}
159 			}
160 
161 			buffer * const tb = r->tmp_buf;
162 			buffer_copy_buffer(&r->physical.basedir, &ds->value);
163 			buffer_copy_buffer(tb, &ds->value);
164 			buffer_append_string(tb, uri_ptr + alias_len);
165 			buffer_copy_buffer(&r->physical.path, tb);
166 
167 			return HANDLER_GO_ON;
168 }
169 
170 
171 int mod_alias_plugin_init(plugin *p);
172 int mod_alias_plugin_init(plugin *p) {
173 	p->version     = LIGHTTPD_VERSION_ID;
174 	p->name        = "alias";
175 
176 	p->init           = mod_alias_init;
177 	p->handle_physical= mod_alias_physical_handler;
178 	p->set_defaults   = mod_alias_set_defaults;
179 
180 	return 0;
181 }
182