1 #include "first.h"
2
3 #include "base.h"
4 #include "burl.h"
5 #include "chunk.h"
6 #include "ck.h"
7 #include "fdevent.h"
8 #include "fdlog.h"
9 #include "http_etag.h"
10 #include "keyvalue.h"
11 #include "log.h"
12
13 #include "configparser.h"
14 #include "configfile.h"
15 #include "plugin.h"
16 #include "reqpool.h"
17 #include "sock_addr.h"
18 #include "stat_cache.h"
19 #include "sys-crypto.h"
20
21 #include <sys/stat.h>
22 #ifdef HAVE_SYS_WAIT_H
23 #include <sys/wait.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <limits.h>
32 #include <glob.h>
33
34 #ifdef HAVE_SYSLOG_H
35 # include <syslog.h>
36 #endif
37
38 #ifdef HAVE_PCRE2_H
39 #define PCRE2_CODE_UNIT_WIDTH 8
40 #include <pcre2.h>
41 #endif
42
43 #ifndef PATH_MAX
44 #define PATH_MAX 4096
45 #endif
46
47 typedef struct {
48 PLUGIN_DATA;
49 request_config defaults;
50 } config_data_base;
51
config_free_config(void * const p_d)52 static void config_free_config(void * const p_d) {
53 plugin_data_base * const p = p_d;
54 if (NULL == p) return;
55 if (NULL == p->cvlist) { free(p); return; }
56 /* (init i to 0 if global context; to 1 to skip empty global context) */
57 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
58 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
59 for (; -1 != cpv->k_id; ++cpv) {
60 switch (cpv->k_id) {
61 case 18:/* server.kbytes-per-second */
62 if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v);
63 break;
64 default:
65 break;
66 }
67 }
68 }
69 free(p->cvlist);
70 free(p);
71 }
72
config_reset_config_bytes_sec(void * const p_d)73 void config_reset_config_bytes_sec(void * const p_d) {
74 plugin_data_base * const p = p_d;
75 if (NULL == p->cvlist) return;
76 /* (init i to 0 if global context; to 1 to skip empty global context) */
77 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
78 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
79 for (; -1 != cpv->k_id; ++cpv) {
80 switch (cpv->k_id) {
81 case 18:/* server.kbytes-per-second */
82 if (cpv->vtype == T_CONFIG_LOCAL) ((off_t *)cpv->v.v)[0] = 0;
83 break;
84 default:
85 break;
86 }
87 }
88 }
89 }
90
config_merge_config_cpv(request_config * const pconf,const config_plugin_value_t * const cpv)91 static void config_merge_config_cpv(request_config * const pconf, const config_plugin_value_t * const cpv) {
92 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
93 case 0: /* server.document-root */
94 pconf->document_root = cpv->v.b;
95 break;
96 case 1: /* server.name */
97 pconf->server_name = cpv->v.b;
98 break;
99 case 2: /* server.tag */
100 pconf->server_tag = cpv->v.b;
101 break;
102 case 3: /* server.max-request-size */
103 pconf->max_request_size = cpv->v.u;
104 break;
105 case 4: /* server.max-keep-alive-requests */
106 pconf->max_keep_alive_requests = cpv->v.shrt;
107 break;
108 case 5: /* server.max-keep-alive-idle */
109 pconf->max_keep_alive_idle = cpv->v.shrt;
110 break;
111 case 6: /* server.max-read-idle */
112 pconf->max_read_idle = cpv->v.shrt;
113 break;
114 case 7: /* server.max-write-idle */
115 pconf->max_write_idle = cpv->v.shrt;
116 break;
117 case 8: /* server.errorfile-prefix */
118 pconf->errorfile_prefix = cpv->v.b;
119 break;
120 case 9: /* server.error-handler */
121 pconf->error_handler = cpv->v.b;
122 break;
123 case 10:/* server.error-handler-404 */
124 pconf->error_handler_404 = cpv->v.b;
125 break;
126 case 11:/* server.error-intercept */
127 pconf->error_intercept = (0 != cpv->v.u);
128 break;
129 case 12:/* server.force-lowercase-filenames */
130 pconf->force_lowercase_filenames = (0 != cpv->v.u);
131 break;
132 case 13:/* server.follow-symlink */
133 pconf->follow_symlink = (0 != cpv->v.u);
134 break;
135 case 14:/* server.protocol-http11 */
136 pconf->allow_http11 = (0 != cpv->v.u);
137 break;
138 case 15:/* server.range-requests */
139 pconf->range_requests = (0 != cpv->v.u);
140 break;
141 case 16:/* server.stream-request-body */
142 pconf->stream_request_body = cpv->v.shrt;
143 break;
144 case 17:/* server.stream-response-body */
145 pconf->stream_response_body = cpv->v.shrt;
146 break;
147 case 18:/* server.kbytes-per-second */
148 pconf->global_bytes_per_second = (unsigned int)((off_t *)cpv->v.v)[1];
149 pconf->global_bytes_per_second_cnt_ptr = cpv->v.v;
150 break;
151 case 19:/* connection.kbytes-per-second */
152 pconf->bytes_per_second = (unsigned int)cpv->v.shrt << 10;/* (*=1024) */
153 break;
154 case 20:/* mimetype.assign */
155 pconf->mimetypes = cpv->v.a;
156 break;
157 case 21:/* mimetype.use-xattr */
158 pconf->use_xattr = (0 != cpv->v.u);
159 break;
160 case 22:/* etag.use-inode */
161 cpv->v.u
162 ? (pconf->etag_flags |= ETAG_USE_INODE)
163 : (pconf->etag_flags &= ~ETAG_USE_INODE);
164 break;
165 case 23:/* etag.use-mtime */
166 cpv->v.u
167 ? (pconf->etag_flags |= ETAG_USE_MTIME)
168 : (pconf->etag_flags &= ~ETAG_USE_MTIME);
169 break;
170 case 24:/* etag.use-size */
171 cpv->v.u
172 ? (pconf->etag_flags |= ETAG_USE_SIZE)
173 : (pconf->etag_flags &= ~ETAG_USE_SIZE);
174 break;
175 case 25:/* debug.log-condition-handling */
176 pconf->log_condition_handling = (0 != cpv->v.u);
177 break;
178 case 26:/* debug.log-file-not-found */
179 pconf->log_file_not_found = (0 != cpv->v.u);
180 break;
181 case 27:/* debug.log-request-handling */
182 pconf->log_request_handling = (0 != cpv->v.u);
183 break;
184 case 28:/* debug.log-request-header */
185 pconf->log_request_header = (0 != cpv->v.u);
186 break;
187 case 29:/* debug.log-response-header */
188 pconf->log_response_header = (0 != cpv->v.u);
189 break;
190 case 30:/* debug.log-timeouts */
191 pconf->log_timeouts = (0 != cpv->v.u);
192 break;
193 case 31:/* debug.log-state-handling */
194 pconf->log_state_handling = (0 != cpv->v.u);
195 break;
196 case 32:/* server.errorlog */
197 if (cpv->vtype == T_CONFIG_LOCAL) pconf->errh = cpv->v.v;
198 break;
199 case 33:/* server.breakagelog */
200 if (cpv->vtype == T_CONFIG_LOCAL) pconf->serrh = cpv->v.v;
201 break;
202 default:/* should not happen */
203 return;
204 }
205 }
206
config_merge_config(request_config * const pconf,const config_plugin_value_t * cpv)207 static void config_merge_config(request_config * const pconf, const config_plugin_value_t *cpv) {
208 do {
209 config_merge_config_cpv(pconf, cpv);
210 } while ((++cpv)->k_id != -1);
211 }
212
config_patch_config(request_st * const r)213 void config_patch_config(request_st * const r) {
214 config_data_base * const p = r->con->config_data_base;
215
216 /* performed by request_config_reset() */
217 /*memcpy(&r->conf, &p->defaults, sizeof(request_config));*/
218
219 for (int i = 1, used = p->nconfig; i < used; ++i) {
220 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
221 config_merge_config(&r->conf, p->cvlist + p->cvlist[i].v.u2[0]);
222 }
223 }
224
225 #if 0 /*(moved to reqpool.c:request_config_reset())*/
226 void config_reset_config(request_st * const r) {
227 /* initialize request_config (r->conf) from top-level request_config */
228 config_data_base * const p = r->con->config_data_base;
229 memcpy(&r->conf, &p->defaults, sizeof(request_config));
230 }
231 #endif
232
config_burl_normalize_cond(server * const srv)233 static void config_burl_normalize_cond (server * const srv) {
234 buffer * const tb = srv->tmp_buf;
235 for (uint32_t i = 0; i < srv->config_context->used; ++i) {
236 data_config * const config =(data_config *)srv->config_context->data[i];
237 if (COMP_HTTP_QUERY_STRING != config->comp) continue;
238 switch(config->cond) {
239 case CONFIG_COND_NE:
240 case CONFIG_COND_EQ:
241 case CONFIG_COND_PREFIX:
242 case CONFIG_COND_SUFFIX:
243 /* (can use this routine as long as it does not perform
244 * any regex-specific normalization of first arg) */
245 pcre_keyvalue_burl_normalize_key(&config->string, tb);
246 break;
247 case CONFIG_COND_NOMATCH:
248 case CONFIG_COND_MATCH:
249 pcre_keyvalue_burl_normalize_key(&config->string, tb);
250 break;
251 default:
252 break;
253 }
254 }
255 }
256
config_pcre_keyvalue(server * const srv)257 static int config_pcre_keyvalue (server * const srv) {
258 const int pcre_jit = config_feature_bool(srv, "server.pcre_jit", 1);
259 for (uint32_t i = 0; i < srv->config_context->used; ++i) {
260 data_config * const dc = (data_config *)srv->config_context->data[i];
261 if (dc->cond != CONFIG_COND_NOMATCH && dc->cond != CONFIG_COND_MATCH)
262 continue;
263 if (!data_config_pcre_compile(dc, pcre_jit, srv->errh))
264 return 0;
265 }
266
267 return 1;
268 }
269
270 #ifdef USE_OPENSSL_CRYPTO
config_warn_openssl_module(server * srv)271 static void config_warn_openssl_module (server *srv) {
272 for (uint32_t i = 0; i < srv->config_context->used; ++i) {
273 const data_config *config = (data_config const*)srv->config_context->data[i];
274 for (uint32_t j = 0; j < config->value->used; ++j) {
275 data_unset *du = config->value->data[j];
276 if (0 == strncmp(du->key.ptr, "ssl.", sizeof("ssl.")-1)) {
277 /* mod_openssl should be loaded after mod_extforward */
278 array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_openssl"));
279 log_error(srv->errh, __FILE__, __LINE__,
280 "Warning: please add \"mod_openssl\" to server.modules list "
281 "in lighttpd.conf. A future release of lighttpd 1.4.x "
282 "*will not* automatically load mod_openssl and lighttpd "
283 "*will not* use SSL/TLS where your lighttpd.conf contains "
284 "ssl.* directives");
285 return;
286 }
287 }
288 }
289 }
290 #endif
291
config_check_module_duplicates(server * srv)292 static void config_check_module_duplicates (server *srv) {
293 int dups = 0;
294 data_string ** const data = (data_string **)srv->srvconf.modules->data;
295 const uint32_t used = srv->srvconf.modules->used;
296 for (uint32_t i = 0; i < used; ++i) {
297 const buffer * const m = &data[i]->value;
298 for (uint32_t j = i+1; j < used; ++j) {
299 if (buffer_is_equal(m, &data[j]->value)) {
300 ++dups;
301 break;
302 }
303 }
304 }
305 if (!dups) return;
306
307 array * const modules = array_init(used - dups);
308 for (uint32_t i = 0; i < used; ++i) {
309 const buffer * const m = &data[i]->value;
310 uint32_t j;
311 for (j = 0; j < modules->used; ++j) {
312 buffer *n = &((data_string *)modules->data[j])->value;
313 if (buffer_is_equal(m, n)) break; /* duplicate */
314 }
315 if (j == modules->used)
316 array_insert_value(modules, BUF_PTR_LEN(m));
317 }
318 array_free(srv->srvconf.modules);
319 srv->srvconf.modules = modules;
320 }
321
322 __attribute_pure__
323 __attribute_noinline__
config_has_opt_enabled(const server * const srv,const char * const opt,const uint32_t olen)324 static int config_has_opt_enabled (const server * const srv, const char * const opt, const uint32_t olen) {
325 for (uint32_t i = 0; i < srv->config_context->used; ++i) {
326 const data_config * const config =
327 (const data_config *)srv->config_context->data[i];
328 const data_unset * const du =
329 array_get_data_unset(config->value, opt, olen);
330 if (NULL == du) continue;
331 if (du->type == TYPE_ARRAY
332 ? ((data_array *)du)->value.used != 0
333 : config_plugin_value_tobool(du, 0))
334 return 1;
335 }
336 return 0;
337 }
338
339 __attribute_pure__
340 __attribute_noinline__
config_has_opt_and_value(const server * const srv,const char * const opt,const uint32_t olen,const char * const v,const uint32_t vlen)341 static const char * config_has_opt_and_value (const server * const srv, const char * const opt, const uint32_t olen, const char * const v, const uint32_t vlen) {
342 for (uint32_t i = 0; i < srv->config_context->used; ++i) {
343 const data_config * const config =
344 (data_config const *)srv->config_context->data[i];
345 const data_string * const ds =
346 (data_string *)array_get_element_klen(config->value, opt, olen);
347 if (NULL != ds && ds->type == TYPE_STRING
348 && buffer_eq_slen(&ds->value, v, vlen))
349 return v;
350 }
351 return NULL;
352 }
353
354 __attribute_noinline__
config_compat_module_remove(server * srv,const char * module,uint32_t len)355 static void config_compat_module_remove (server *srv, const char *module, uint32_t len) {
356 array *modules = array_init(srv->srvconf.modules->used);
357
358 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
359 const data_string *ds = (data_string *)srv->srvconf.modules->data[i];
360 if (!buffer_eq_slen(&ds->value, module, len))
361 array_insert_value(modules, BUF_PTR_LEN(&ds->value));
362 }
363
364 array_free(srv->srvconf.modules);
365 srv->srvconf.modules = modules;
366 }
367
368 __attribute_noinline__
config_compat_module_prepend(server * srv,const char * module,uint32_t len)369 static void config_compat_module_prepend (server *srv, const char *module, uint32_t len) {
370 array *modules = array_init(srv->srvconf.modules->used+4);
371 array_insert_value(modules, module, len);
372
373 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
374 data_string *ds = (data_string *)srv->srvconf.modules->data[i];
375 array_insert_value(modules, BUF_PTR_LEN(&ds->value));
376 }
377
378 array_free(srv->srvconf.modules);
379 srv->srvconf.modules = modules;
380 }
381
config_warn_authn_module(server * srv,const char * module,uint32_t len,const char * v)382 static void config_warn_authn_module (server *srv, const char *module, uint32_t len, const char *v) {
383 buffer * const tb = srv->tmp_buf;
384 buffer_copy_string_len(tb, CONST_STR_LEN("mod_authn_"));
385 buffer_append_string_len(tb, module, len);
386 array_insert_value(srv->srvconf.modules, BUF_PTR_LEN(tb));
387 log_error(srv->errh, __FILE__, __LINE__,
388 "Warning: please add \"mod_authn_%s\" to server.modules list "
389 "in lighttpd.conf. A future release of lighttpd 1.4.x will "
390 "not automatically load mod_authn_%s and lighttpd will fail "
391 "to start up since your lighttpd.conf uses auth.backend = \"%s\".",
392 module, module, v);
393 }
394
config_compat_module_load(server * srv)395 static void config_compat_module_load (server *srv) {
396 int prepend_mod_indexfile = 1;
397 int append_mod_dirlisting = 1;
398 int append_mod_staticfile = 1;
399 int append_mod_authn_file = 1;
400 int append_mod_authn_ldap = 1;
401 int append_mod_openssl = 1;
402 int contains_mod_auth = 0;
403 int prepend_mod_auth = 0;
404 int prepend_mod_vhostdb = 0;
405 const char *dyn_name = NULL;
406
407 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
408 buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value;
409
410 if (buffer_eq_slen(m, CONST_STR_LEN("mod_indexfile")))
411 prepend_mod_indexfile = 0;
412 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_staticfile")))
413 append_mod_staticfile = 0;
414 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_dirlisting")))
415 append_mod_dirlisting = 0;
416 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_gnutls")))
417 append_mod_openssl = 0;
418 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_mbedtls")))
419 append_mod_openssl = 0;
420 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_nss")))
421 append_mod_openssl = 0;
422 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_openssl")))
423 append_mod_openssl = 0;
424 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_wolfssl")))
425 append_mod_openssl = 0;
426 else if (0 == strncmp(m->ptr, "mod_auth", sizeof("mod_auth")-1)) {
427 if (buffer_eq_slen(m, CONST_STR_LEN("mod_auth"))) {
428 if (!contains_mod_auth) {
429 contains_mod_auth = 1;
430 if (dyn_name)
431 log_error(srv->errh, __FILE__, __LINE__,
432 "Warning: mod_auth should be listed in server.modules"
433 " before dynamic backends such as %s", dyn_name);
434 }
435 }
436 else if (!contains_mod_auth)
437 prepend_mod_auth = 1;
438
439 if (buffer_eq_slen(m, CONST_STR_LEN("mod_authn_file")))
440 append_mod_authn_file = 0;
441 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_authn_ldap")))
442 append_mod_authn_ldap = 0;
443 }
444 else if (0 == strncmp(m->ptr, "mod_vhostdb", sizeof("mod_vhostdb")-1)) {
445 if (buffer_eq_slen(m, CONST_STR_LEN("mod_vhostdb")))
446 prepend_mod_vhostdb |= 2;
447 else if (!(prepend_mod_vhostdb & 2))
448 prepend_mod_vhostdb |= 1;
449 }
450 else if ( 0 == strncmp(m->ptr, "mod_ajp13",
451 sizeof("mod_ajp13")-1)
452 || 0 == strncmp(m->ptr, "mod_cgi",
453 sizeof("mod_cgi")-1)
454 || 0 == strncmp(m->ptr, "mod_fastcgi",
455 sizeof("mod_fastcgi")-1)
456 || 0 == strncmp(m->ptr, "mod_proxy",
457 sizeof("mod_proxy")-1)
458 || 0 == strncmp(m->ptr, "mod_scgi",
459 sizeof("mod_scgi")-1)
460 || 0 == strncmp(m->ptr, "mod_sockproxy",
461 sizeof("mod_sockproxy")-1)
462 || 0 == strncmp(m->ptr, "mod_wstunnel",
463 sizeof("mod_wstunnel")-1)) {
464 if (NULL == dyn_name)
465 dyn_name = m->ptr;
466 }
467 }
468
469 /* check if some default modules are used and enabled
470 * (Each dynamically loaded modules takes at least 20k memory,
471 * so avoid loading some default modules unless used and enabled) */
472
473 if (!config_has_opt_enabled(srv, CONST_STR_LEN("index-file.names"))
474 && !config_has_opt_enabled(srv, CONST_STR_LEN("server.indexfiles"))) {
475 if (!prepend_mod_indexfile)
476 config_compat_module_remove(srv, CONST_STR_LEN("mod_indexfile"));
477 prepend_mod_indexfile = 0;
478 }
479
480 if (!config_has_opt_enabled(srv, CONST_STR_LEN("dir-listing.activate"))
481 && !config_has_opt_enabled(srv, CONST_STR_LEN("server.dir-listing"))) {
482 if (!append_mod_dirlisting)
483 config_compat_module_remove(srv, CONST_STR_LEN("mod_dirlisting"));
484 append_mod_dirlisting = 0;
485 }
486
487 /* prepend default modules */
488
489 if (prepend_mod_indexfile) {
490 /* mod_indexfile has to be loaded before mod_fastcgi and friends */
491 config_compat_module_prepend(srv, CONST_STR_LEN("mod_indexfile"));
492 }
493
494 /* append default modules */
495
496 if (append_mod_dirlisting) {
497 array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_dirlisting"));
498 }
499
500 if (append_mod_staticfile) {
501 array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_staticfile"));
502 }
503
504 if (append_mod_openssl) {
505 #ifdef USE_OPENSSL_CRYPTO
506 config_warn_openssl_module(srv);
507 #endif
508 }
509
510 /* mod_auth.c,mod_auth_api.c auth backends were split into separate modules
511 * Automatically load auth backend modules for compatibility with
512 * existing lighttpd 1.4.x configs */
513 if (contains_mod_auth) {
514 if (append_mod_authn_file) {
515 const char *v;
516 if ( (v=config_has_opt_and_value(srv,CONST_STR_LEN("auth.backend"),
517 CONST_STR_LEN("htdigest")))
518 ||(v=config_has_opt_and_value(srv,CONST_STR_LEN("auth.backend"),
519 CONST_STR_LEN("htpasswd")))
520 ||(v=config_has_opt_and_value(srv,CONST_STR_LEN("auth.backend"),
521 CONST_STR_LEN("plain"))))
522 config_warn_authn_module(srv, CONST_STR_LEN("file"), v);
523 }
524 if (append_mod_authn_ldap) {
525 #if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER)
526 if (config_has_opt_and_value(srv, CONST_STR_LEN("auth.backend"),
527 CONST_STR_LEN("ldap")))
528 config_warn_authn_module(srv, CONST_STR_LEN("ldap"), "ldap");
529 #endif
530 }
531 }
532
533 if (prepend_mod_auth) {
534 config_compat_module_prepend(srv, CONST_STR_LEN("mod_auth"));
535 }
536
537 if (prepend_mod_vhostdb & 1) {
538 config_compat_module_prepend(srv, CONST_STR_LEN("mod_vhostdb"));
539 }
540 }
541
config_deprecate_module_compress(server * srv)542 static void config_deprecate_module_compress (server *srv) {
543 int mod_compress_idx = -1;
544 int mod_deflate_idx = -1;
545 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
546 buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value;
547 if (buffer_eq_slen(m, CONST_STR_LEN("mod_compress")))
548 mod_compress_idx = (int)i;
549 else if (buffer_eq_slen(m, CONST_STR_LEN("mod_deflate")))
550 mod_deflate_idx = (int)i;
551 }
552 if (mod_compress_idx < 0) return;
553
554 int has_compress_directive = 0;
555 for (uint32_t i = 0; i < srv->config_context->used; ++i) {
556 const data_config *config =
557 (data_config const *)srv->config_context->data[i];
558 for (uint32_t j = 0; j < config->value->used; ++j) {
559 buffer *k = &config->value->data[j]->key;
560 if (0 == strncmp(k->ptr, "compress.", sizeof("compress.")-1)) {
561 has_compress_directive = 1;
562 break;
563 }
564 }
565 if (has_compress_directive) {
566 log_error(srv->errh, __FILE__, __LINE__,
567 "Warning: \"mod_compress\" is DEPRECATED and has been replaced "
568 "with \"mod_deflate\". A future release of lighttpd 1.4.x will "
569 "not contain mod_compress and lighttpd may fail to start up");
570 break;
571 }
572 }
573
574 if (mod_deflate_idx >= 0 || !has_compress_directive) {
575 /* create new modules value list without mod_compress */
576 array *a = array_init(srv->srvconf.modules->used-1);
577 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
578 buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value;
579 if (buffer_eq_slen(m, CONST_STR_LEN("mod_compress")))
580 continue;
581 array_insert_value(a, BUF_PTR_LEN(m));
582 }
583 array_free(srv->srvconf.modules);
584 srv->srvconf.modules = a;
585 }
586 else {
587 /* replace "mod_compress" value with "mod_deflate" value */
588 buffer *m = &((data_string *)srv->srvconf.modules->data[mod_compress_idx])->value;
589 buffer_copy_string_len(m, CONST_STR_LEN("mod_deflate"));
590 }
591 }
592
config_http_parseopts(server * srv,const array * a)593 static int config_http_parseopts (server *srv, const array *a) {
594 unsigned short int opts = srv->srvconf.http_url_normalize;
595 unsigned short int decode_2f = 1;
596 int rc = 1;
597 for (size_t i = 0; i < a->used; ++i) {
598 const data_string * const ds = (const data_string *)a->data[i];
599 const buffer *k = &ds->key;
600 unsigned short int opt;
601 int val = config_plugin_value_tobool((data_unset *)ds, 2);
602 if (2 == val) {
603 log_error(srv->errh, __FILE__, __LINE__,
604 "unrecognized value for server.http-parseopts: "
605 "%s => %s (expect \"[enable|disable]\")", k->ptr, ds->value.ptr);
606 rc = 0;
607 }
608 if (buffer_eq_slen(k, CONST_STR_LEN("url-normalize")))
609 opt = HTTP_PARSEOPT_URL_NORMALIZE;
610 else if (buffer_eq_slen(k, CONST_STR_LEN("url-normalize-unreserved")))
611 opt = HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED;
612 else if (buffer_eq_slen(k, CONST_STR_LEN("url-normalize-required")))
613 opt = HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED;
614 else if (buffer_eq_slen(k, CONST_STR_LEN("url-ctrls-reject")))
615 opt = HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT;
616 else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-backslash-trans")))
617 opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_BACKSLASH_TRANS;
618 else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-2f-decode")))
619 opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE;
620 else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-2f-reject")))
621 opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT;
622 else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-dotseg-remove")))
623 opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE;
624 else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-dotseg-reject")))
625 opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT;
626 else if (buffer_eq_slen(k, CONST_STR_LEN("url-query-20-plus")))
627 opt = HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS;
628 else if (buffer_eq_slen(k, CONST_STR_LEN("url-invalid-utf8-reject")))
629 opt = HTTP_PARSEOPT_URL_NORMALIZE_INVALID_UTF8_REJECT;
630 else if (buffer_eq_slen(k, CONST_STR_LEN("header-strict"))) {
631 srv->srvconf.http_header_strict = val;
632 continue;
633 }
634 else if (buffer_eq_slen(k, CONST_STR_LEN("host-strict"))) {
635 srv->srvconf.http_host_strict = val;
636 continue;
637 }
638 else if (buffer_eq_slen(k, CONST_STR_LEN("host-normalize"))) {
639 srv->srvconf.http_host_normalize = val;
640 continue;
641 }
642 else if (buffer_eq_slen(k, CONST_STR_LEN("method-get-body"))) {
643 srv->srvconf.http_method_get_body = val;
644 continue;
645 }
646 else {
647 log_error(srv->errh, __FILE__, __LINE__,
648 "unrecognized key for server.http-parseopts: %s", k->ptr);
649 rc = 0;
650 continue;
651 }
652 if (val)
653 opts |= opt;
654 else {
655 opts &= ~opt;
656 if (opt == HTTP_PARSEOPT_URL_NORMALIZE) {
657 opts = 0;
658 break;
659 }
660 if (opt == HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE) {
661 decode_2f = 0;
662 }
663 }
664 }
665 if (opts != 0) {
666 opts |= HTTP_PARSEOPT_URL_NORMALIZE;
667 if ((opts & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE
668 |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT))
669 == (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE
670 |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) {
671 log_error(srv->errh, __FILE__, __LINE__,
672 "conflicting options in server.http-parseopts:"
673 "url-path-2f-decode, url-path-2f-reject");
674 rc = 0;
675 }
676 if ((opts & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE
677 |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT))
678 == (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE
679 |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT)) {
680 log_error(srv->errh, __FILE__, __LINE__,
681 "conflicting options in server.http-parseopts:"
682 "url-path-dotseg-remove, url-path-dotseg-reject");
683 rc = 0;
684 }
685 if (!(opts & (HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED
686 |HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED))) {
687 opts |= HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED
688 | HTTP_PARSEOPT_URL_NORMALIZE_INVALID_UTF8_REJECT;
689 if (decode_2f
690 && !(opts & HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT))
691 opts |= HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE;
692 }
693 }
694 srv->srvconf.http_url_normalize = opts;
695 return rc;
696 }
697
config_insert_srvconf(server * srv)698 static int config_insert_srvconf(server *srv) {
699 static const config_plugin_keys_t cpk[] = {
700 { CONST_STR_LEN("server.modules"),
701 T_CONFIG_ARRAY_VLIST,
702 T_CONFIG_SCOPE_SERVER }
703 ,{ CONST_STR_LEN("server.compat-module-load"),
704 T_CONFIG_BOOL,
705 T_CONFIG_SCOPE_SERVER }
706 ,{ CONST_STR_LEN("server.systemd-socket-activation"),
707 T_CONFIG_BOOL,
708 T_CONFIG_SCOPE_SERVER }
709 ,{ CONST_STR_LEN("server.port"),
710 T_CONFIG_SHORT,
711 T_CONFIG_SCOPE_SERVER }
712 ,{ CONST_STR_LEN("server.bind"),
713 T_CONFIG_STRING,
714 T_CONFIG_SCOPE_SERVER }
715 ,{ CONST_STR_LEN("server.network-backend"),
716 T_CONFIG_STRING,
717 T_CONFIG_SCOPE_SERVER }
718 ,{ CONST_STR_LEN("server.chroot"),
719 T_CONFIG_STRING,
720 T_CONFIG_SCOPE_SERVER }
721 ,{ CONST_STR_LEN("server.username"),
722 T_CONFIG_STRING,
723 T_CONFIG_SCOPE_SERVER }
724 ,{ CONST_STR_LEN("server.groupname"),
725 T_CONFIG_STRING,
726 T_CONFIG_SCOPE_SERVER }
727 ,{ CONST_STR_LEN("server.errorlog-placeholder-moved-to-config-insert"),
728 T_CONFIG_STRING,
729 T_CONFIG_SCOPE_SERVER }
730 ,{ CONST_STR_LEN("server.breakagelog-placeholder-moved-to-config-insert"),
731 T_CONFIG_STRING,
732 T_CONFIG_SCOPE_SERVER }
733 ,{ CONST_STR_LEN("server.errorlog-use-syslog"),
734 T_CONFIG_BOOL,
735 T_CONFIG_SCOPE_SERVER }
736 ,{ CONST_STR_LEN("server.syslog-facility"),
737 T_CONFIG_STRING,
738 T_CONFIG_SCOPE_SERVER }
739 ,{ CONST_STR_LEN("server.core-files"),
740 T_CONFIG_BOOL,
741 T_CONFIG_SCOPE_SERVER }
742 ,{ CONST_STR_LEN("server.event-handler"),
743 T_CONFIG_STRING,
744 T_CONFIG_SCOPE_SERVER }
745 ,{ CONST_STR_LEN("server.pid-file"),
746 T_CONFIG_STRING,
747 T_CONFIG_SCOPE_SERVER }
748 ,{ CONST_STR_LEN("server.max-worker"),
749 T_CONFIG_SHORT,
750 T_CONFIG_SCOPE_SERVER }
751 ,{ CONST_STR_LEN("server.max-fds"),
752 T_CONFIG_SHORT,
753 T_CONFIG_SCOPE_SERVER }
754 ,{ CONST_STR_LEN("server.max-connections"),
755 T_CONFIG_SHORT,
756 T_CONFIG_SCOPE_SERVER }
757 ,{ CONST_STR_LEN("server.max-request-field-size"),
758 T_CONFIG_INT,
759 T_CONFIG_SCOPE_SERVER }
760 ,{ CONST_STR_LEN("server.chunkqueue-chunk-sz"),
761 T_CONFIG_INT,
762 T_CONFIG_SCOPE_SERVER }
763 ,{ CONST_STR_LEN("server.upload-temp-file-size"),
764 T_CONFIG_INT,
765 T_CONFIG_SCOPE_SERVER }
766 ,{ CONST_STR_LEN("server.upload-dirs"),
767 T_CONFIG_ARRAY_VLIST,
768 T_CONFIG_SCOPE_SERVER }
769 ,{ CONST_STR_LEN("server.http-parseopts"),
770 T_CONFIG_ARRAY_KVSTRING,
771 T_CONFIG_SCOPE_SERVER }
772 ,{ CONST_STR_LEN("server.http-parseopt-header-strict"),
773 T_CONFIG_BOOL,
774 T_CONFIG_SCOPE_SERVER }
775 ,{ CONST_STR_LEN("server.http-parseopt-host-strict"),
776 T_CONFIG_BOOL,
777 T_CONFIG_SCOPE_SERVER }
778 ,{ CONST_STR_LEN("server.http-parseopt-host-normalize"),
779 T_CONFIG_BOOL,
780 T_CONFIG_SCOPE_SERVER }
781 ,{ CONST_STR_LEN("server.reject-expect-100-with-417"), /*(ignored)*/
782 T_CONFIG_BOOL,
783 T_CONFIG_SCOPE_SERVER }
784 ,{ CONST_STR_LEN("server.stat-cache-engine"),
785 T_CONFIG_STRING,
786 T_CONFIG_SCOPE_SERVER }
787 ,{ CONST_STR_LEN("mimetype.xattr-name"),
788 T_CONFIG_STRING,
789 T_CONFIG_SCOPE_SERVER }
790 ,{ CONST_STR_LEN("ssl.engine"),
791 T_CONFIG_BOOL,
792 T_CONFIG_SCOPE_SOCKET }
793 ,{ CONST_STR_LEN("debug.log-request-header-on-error"),
794 T_CONFIG_BOOL,
795 T_CONFIG_SCOPE_SERVER }
796 ,{ CONST_STR_LEN("server.feature-flags"),
797 T_CONFIG_ARRAY_KVANY,
798 T_CONFIG_SCOPE_SERVER }
799 ,{ NULL, 0,
800 T_CONFIG_UNSET,
801 T_CONFIG_SCOPE_UNSET }
802 };
803
804 srv->srvconf.h2proto = 2; /* enable HTTP/2 and h2c by default */
805
806 int rc = 0;
807 plugin_data_base srvplug;
808 memset(&srvplug, 0, sizeof(srvplug));
809 plugin_data_base * const p = &srvplug;
810 if (!config_plugin_values_init(srv, p, cpk, "global"))
811 return HANDLER_ERROR;
812
813 int ssl_enabled = 0; /*(directive checked here only to set default port)*/
814
815 /* process and validate T_CONFIG_SCOPE_SERVER config directives */
816 if (p->cvlist[0].v.u2[1]) {
817 config_plugin_value_t *cpv = p->cvlist + p->cvlist[0].v.u2[0];
818 for (; -1 != cpv->k_id; ++cpv) {
819 switch (cpv->k_id) {
820 case 0: /* server.modules */
821 array_copy_array(srv->srvconf.modules, cpv->v.a);
822 break;
823 case 1: /* server.compat-module-load */
824 srv->srvconf.compat_module_load = (unsigned short)cpv->v.u;
825 break;
826 case 2: /* server.systemd-socket-activation */
827 srv->srvconf.systemd_socket_activation=(unsigned short)cpv->v.u;
828 break;
829 case 3: /* server.port */
830 srv->srvconf.port = cpv->v.shrt;
831 break;
832 case 4: /* server.bind */
833 if (!buffer_is_blank(cpv->v.b))
834 srv->srvconf.bindhost = cpv->v.b;
835 break;
836 case 5: /* server.network-backend */
837 if (!buffer_is_blank(cpv->v.b))
838 srv->srvconf.network_backend = cpv->v.b;
839 break;
840 case 6: /* server.chroot */
841 if (!buffer_is_blank(cpv->v.b))
842 srv->srvconf.changeroot = cpv->v.b;
843 break;
844 case 7: /* server.username */
845 if (!buffer_is_blank(cpv->v.b))
846 srv->srvconf.username = cpv->v.b;
847 break;
848 case 8: /* server.groupname */
849 if (!buffer_is_blank(cpv->v.b))
850 srv->srvconf.groupname = cpv->v.b;
851 break;
852 case 9: /* server.errorlog */ /* moved to config_insert() */
853 /*srv->srvconf.errorlog_file = cpv->v.b;*/
854 break;
855 case 10:/* server.breakagelog */ /* moved to config_insert() */
856 /*srv->srvconf.breakagelog_file = cpv->v.b;*/
857 break;
858 case 11:/* server.errorlog-use-syslog */
859 srv->srvconf.errorlog_use_syslog = (unsigned short)cpv->v.u;
860 break;
861 case 12:/* server.syslog-facility */
862 if (!buffer_is_blank(cpv->v.b))
863 srv->srvconf.syslog_facility = cpv->v.b;
864 break;
865 case 13:/* server.core-files */
866 srv->srvconf.enable_cores = (unsigned short)cpv->v.u;
867 break;
868 case 14:/* server.event-handler */
869 srv->srvconf.event_handler = cpv->v.b->ptr;
870 break;
871 case 15:/* server.pid-file */
872 if (!buffer_is_blank(cpv->v.b))
873 *(const buffer **)&srv->srvconf.pid_file = cpv->v.b;
874 break;
875 case 16:/* server.max-worker */
876 srv->srvconf.max_worker = (unsigned short)cpv->v.u;
877 break;
878 case 17:/* server.max-fds */
879 srv->srvconf.max_fds = (unsigned short)cpv->v.u;
880 break;
881 case 18:/* server.max-connections */
882 srv->srvconf.max_conns = (unsigned short)cpv->v.u;
883 break;
884 case 19:/* server.max-request-field-size */
885 srv->srvconf.max_request_field_size = cpv->v.u;
886 break;
887 case 20:/* server.chunkqueue-chunk-sz */
888 chunkqueue_set_chunk_size(cpv->v.u);
889 break;
890 case 21:/* server.upload-temp-file-size */
891 srv->srvconf.upload_temp_file_size = cpv->v.u;
892 break;
893 case 22:/* server.upload-dirs */
894 array_copy_array(srv->srvconf.upload_tempdirs, cpv->v.a);
895 break;
896 case 23:/* server.http-parseopts */
897 if (!config_http_parseopts(srv, cpv->v.a))
898 rc = HANDLER_ERROR;
899 break;
900 case 24:/* server.http-parseopt-header-strict */
901 srv->srvconf.http_header_strict = (0 != cpv->v.u);
902 break;
903 case 25:/* server.http-parseopt-host-strict */
904 srv->srvconf.http_host_strict = (0 != cpv->v.u);
905 break;
906 case 26:/* server.http-parseopt-host-normalize */
907 srv->srvconf.http_host_normalize = (0 != cpv->v.u);
908 break;
909 case 27:/* server.reject-expect-100-with-417 *//*(ignored)*/
910 break;
911 case 28:/* server.stat-cache-engine */
912 if (0 != stat_cache_choose_engine(cpv->v.b, srv->errh))
913 rc = HANDLER_ERROR;
914 break;
915 case 29:/* mimetype.xattr-name */
916 stat_cache_xattrname(cpv->v.b->ptr);
917 break;
918 case 30:/* ssl.engine */
919 ssl_enabled = (0 != cpv->v.u);
920 #if !defined(USE_OPENSSL_CRYPTO) \
921 && !defined(USE_MBEDTLS_CRYPTO) \
922 && !defined(USE_NSS_CRYPTO) \
923 && !defined(USE_GNUTLS_CRYPTO) \
924 && !defined(USE_WOLFSSL_CRYPTO)
925 if (ssl_enabled) {
926 log_error(srv->errh, __FILE__, __LINE__,
927 "ssl support is missing; "
928 "recompile with e.g. --with-openssl");
929 rc = HANDLER_ERROR;
930 break;
931 }
932 #endif
933 break;
934 case 31:/* debug.log-request-header-on-error */
935 srv->srvconf.log_request_header_on_error = (0 != cpv->v.u);
936 break;
937 case 32:/* server.feature-flags */
938 srv->srvconf.feature_flags = cpv->v.a;
939 srv->srvconf.h2proto =
940 config_plugin_value_tobool(
941 array_get_element_klen(cpv->v.a,
942 CONST_STR_LEN("server.h2proto")), 1);
943 if (srv->srvconf.h2proto)
944 srv->srvconf.h2proto +=
945 config_plugin_value_tobool(
946 array_get_element_klen(cpv->v.a,
947 CONST_STR_LEN("server.h2c")), 1);
948 srv->srvconf.absolute_dir_redirect =
949 config_plugin_value_tobool(
950 array_get_element_klen(cpv->v.a,
951 CONST_STR_LEN("server.absolute-dir-redirect")), 0);
952 break;
953 default:/* should not happen */
954 break;
955 }
956 }
957 }
958
959 if (0 == srv->srvconf.port)
960 srv->srvconf.port = ssl_enabled ? 443 : 80;
961
962 config_check_module_duplicates(srv);
963
964 if (srv->srvconf.compat_module_load)
965 config_compat_module_load(srv);
966
967 config_deprecate_module_compress(srv);
968
969 if (srv->srvconf.http_url_normalize)
970 config_burl_normalize_cond(srv);
971
972 if (!config_pcre_keyvalue(srv))
973 rc = HANDLER_ERROR;
974
975 free(srvplug.cvlist);
976 return rc;
977 }
978
config_insert(server * srv)979 static int config_insert(server *srv) {
980 static const config_plugin_keys_t cpk[] = {
981 { CONST_STR_LEN("server.document-root"),
982 T_CONFIG_STRING,
983 T_CONFIG_SCOPE_CONNECTION }
984 ,{ CONST_STR_LEN("server.name"),
985 T_CONFIG_STRING,
986 T_CONFIG_SCOPE_CONNECTION }
987 ,{ CONST_STR_LEN("server.tag"),
988 T_CONFIG_STRING,
989 T_CONFIG_SCOPE_CONNECTION }
990 ,{ CONST_STR_LEN("server.max-request-size"),
991 T_CONFIG_INT,
992 T_CONFIG_SCOPE_CONNECTION }
993 ,{ CONST_STR_LEN("server.max-keep-alive-requests"),
994 T_CONFIG_SHORT,
995 T_CONFIG_SCOPE_CONNECTION }
996 ,{ CONST_STR_LEN("server.max-keep-alive-idle"),
997 T_CONFIG_SHORT,
998 T_CONFIG_SCOPE_CONNECTION }
999 ,{ CONST_STR_LEN("server.max-read-idle"),
1000 T_CONFIG_SHORT,
1001 T_CONFIG_SCOPE_CONNECTION }
1002 ,{ CONST_STR_LEN("server.max-write-idle"),
1003 T_CONFIG_SHORT,
1004 T_CONFIG_SCOPE_CONNECTION }
1005 ,{ CONST_STR_LEN("server.errorfile-prefix"),
1006 T_CONFIG_STRING,
1007 T_CONFIG_SCOPE_CONNECTION }
1008 ,{ CONST_STR_LEN("server.error-handler"),
1009 T_CONFIG_STRING,
1010 T_CONFIG_SCOPE_CONNECTION }
1011 ,{ CONST_STR_LEN("server.error-handler-404"),
1012 T_CONFIG_STRING,
1013 T_CONFIG_SCOPE_CONNECTION }
1014 ,{ CONST_STR_LEN("server.error-intercept"),
1015 T_CONFIG_BOOL,
1016 T_CONFIG_SCOPE_CONNECTION }
1017 ,{ CONST_STR_LEN("server.force-lowercase-filenames"),
1018 T_CONFIG_BOOL,
1019 T_CONFIG_SCOPE_CONNECTION }
1020 ,{ CONST_STR_LEN("server.follow-symlink"),
1021 T_CONFIG_BOOL,
1022 T_CONFIG_SCOPE_CONNECTION }
1023 ,{ CONST_STR_LEN("server.protocol-http11"),
1024 T_CONFIG_BOOL,
1025 T_CONFIG_SCOPE_CONNECTION }
1026 ,{ CONST_STR_LEN("server.range-requests"),
1027 T_CONFIG_BOOL,
1028 T_CONFIG_SCOPE_CONNECTION }
1029 ,{ CONST_STR_LEN("server.stream-request-body"),
1030 T_CONFIG_SHORT,
1031 T_CONFIG_SCOPE_CONNECTION }
1032 ,{ CONST_STR_LEN("server.stream-response-body"),
1033 T_CONFIG_SHORT,
1034 T_CONFIG_SCOPE_CONNECTION }
1035 ,{ CONST_STR_LEN("server.kbytes-per-second"),
1036 T_CONFIG_SHORT,
1037 T_CONFIG_SCOPE_CONNECTION }
1038 ,{ CONST_STR_LEN("connection.kbytes-per-second"),
1039 T_CONFIG_SHORT,
1040 T_CONFIG_SCOPE_CONNECTION }
1041 ,{ CONST_STR_LEN("mimetype.assign"),
1042 T_CONFIG_ARRAY_KVSTRING,
1043 T_CONFIG_SCOPE_CONNECTION }
1044 ,{ CONST_STR_LEN("mimetype.use-xattr"),
1045 T_CONFIG_BOOL,
1046 T_CONFIG_SCOPE_CONNECTION }
1047 ,{ CONST_STR_LEN("etag.use-inode"),
1048 T_CONFIG_BOOL,
1049 T_CONFIG_SCOPE_CONNECTION }
1050 ,{ CONST_STR_LEN("etag.use-mtime"),
1051 T_CONFIG_BOOL,
1052 T_CONFIG_SCOPE_CONNECTION }
1053 ,{ CONST_STR_LEN("etag.use-size"),
1054 T_CONFIG_BOOL,
1055 T_CONFIG_SCOPE_CONNECTION }
1056 ,{ CONST_STR_LEN("debug.log-condition-handling"),
1057 T_CONFIG_BOOL,
1058 T_CONFIG_SCOPE_CONNECTION }
1059 ,{ CONST_STR_LEN("debug.log-file-not-found"),
1060 T_CONFIG_BOOL,
1061 T_CONFIG_SCOPE_CONNECTION }
1062 ,{ CONST_STR_LEN("debug.log-request-handling"),
1063 T_CONFIG_BOOL,
1064 T_CONFIG_SCOPE_CONNECTION }
1065 ,{ CONST_STR_LEN("debug.log-request-header"),
1066 T_CONFIG_BOOL,
1067 T_CONFIG_SCOPE_CONNECTION }
1068 ,{ CONST_STR_LEN("debug.log-response-header"),
1069 T_CONFIG_BOOL,
1070 T_CONFIG_SCOPE_CONNECTION }
1071 ,{ CONST_STR_LEN("debug.log-timeouts"),
1072 T_CONFIG_BOOL,
1073 T_CONFIG_SCOPE_CONNECTION }
1074 ,{ CONST_STR_LEN("debug.log-state-handling"),
1075 T_CONFIG_BOOL,
1076 T_CONFIG_SCOPE_CONNECTION }
1077 ,{ CONST_STR_LEN("server.errorlog"),
1078 T_CONFIG_STRING,
1079 T_CONFIG_SCOPE_CONNECTION }
1080 ,{ CONST_STR_LEN("server.breakagelog"),
1081 T_CONFIG_STRING,
1082 T_CONFIG_SCOPE_CONNECTION }
1083 ,{ NULL, 0,
1084 T_CONFIG_UNSET,
1085 T_CONFIG_SCOPE_UNSET }
1086 };
1087
1088 int rc = 0;
1089 config_data_base * const p = ck_calloc(1, sizeof(config_data_base));
1090 srv->config_data_base = p;
1091
1092 if (!config_plugin_values_init(srv, p, cpk, "base"))
1093 return HANDLER_ERROR;
1094
1095 /* process and validate T_CONFIG_SCOPE_CONNECTION config directives
1096 * (init i to 0 if global context; to 1 to skip empty global context) */
1097 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
1098 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
1099 for (; -1 != cpv->k_id; ++cpv) {
1100 switch (cpv->k_id) {
1101 case 0: /* server.document-root */
1102 break;
1103 case 1: /* server.name */
1104 if (buffer_is_blank(cpv->v.b))
1105 cpv->v.b = NULL;
1106 break;
1107 case 2: /* server.tag */
1108 if (!buffer_is_blank(cpv->v.b)) {
1109 buffer *b;
1110 *(const buffer **)&b = cpv->v.b;
1111 for (char *t=strchr(b->ptr,'\n'); t; t=strchr(t+2,'\n')) {
1112 /* not expecting admin to define multi-line server.tag,
1113 * but ensure server_tag has proper header continuation,
1114 * if needed */
1115 if (t[1] == ' ' || t[1] == '\t') continue;
1116 off_t off = t - b->ptr;
1117 size_t len = buffer_clen(b);
1118 buffer_string_prepare_append(b, 1);
1119 t = b->ptr+off;
1120 memmove(t+2, t+1, len - off - 1);
1121 t[1] = ' ';
1122 buffer_commit(b, 1);
1123 }
1124 char *t = b->ptr; /*(make empty if tag is whitespace-only)*/
1125 while (*t==' ' || *t=='\t' || *t=='\r' || *t=='\n') ++t;
1126 if (*t == '\0') buffer_truncate(b, 0);
1127 if (buffer_is_blank(b) && 0 != i)
1128 cpv->v.b = NULL;
1129 else { /* prep for use by h2.c:h2_send_headers() */
1130 buffer_string_prepare_append(b, 6);
1131 memcpy(b->ptr+buffer_clen(b)+1, "server", 6);
1132 }
1133 }
1134 else if (0 != i)
1135 cpv->v.b = NULL;
1136 break;
1137 case 3: /* server.max-request-size */
1138 case 4: /* server.max-keep-alive-requests */
1139 case 5: /* server.max-keep-alive-idle */
1140 case 6: /* server.max-read-idle */
1141 case 7: /* server.max-write-idle */
1142 break;
1143 case 8: /* server.errorfile-prefix */
1144 case 9: /* server.error-handler */
1145 case 10:/* server.error-handler-404 */
1146 if (buffer_is_blank(cpv->v.b))
1147 cpv->v.b = NULL;
1148 break;
1149 case 11:/* server.error-intercept */
1150 case 12:/* server.force-lowercase-filenames */
1151 break;
1152 case 13:/* server.follow-symlink */
1153 #ifndef HAVE_LSTAT
1154 if (0 == cpv->v.u)
1155 log_error(srv->errh, __FILE__, __LINE__,
1156 "Your system lacks lstat(). "
1157 "We can not differ symlinks from files. "
1158 "Please remove server.follow-symlinks from your config.");
1159 #endif
1160 break;
1161 case 14:/* server.protocol-http11 */
1162 case 15:/* server.range-requests */
1163 break;
1164 case 16:/* server.stream-request-body */
1165 if (cpv->v.shrt & FDEVENT_STREAM_REQUEST_BUFMIN)
1166 cpv->v.shrt |=FDEVENT_STREAM_REQUEST;
1167 break;
1168 case 17:/* server.stream-response-body */
1169 if (cpv->v.shrt & FDEVENT_STREAM_RESPONSE_BUFMIN)
1170 cpv->v.shrt |=FDEVENT_STREAM_RESPONSE;
1171 break;
1172 case 18:{/*server.kbytes-per-second */
1173 off_t * const cnt = ck_malloc(2*sizeof(off_t));
1174 cnt[0] = 0;
1175 cnt[1] = (off_t)cpv->v.shrt << 10;
1176 cpv->v.v = cnt;
1177 cpv->vtype = T_CONFIG_LOCAL;
1178 break;
1179 }
1180 case 19:/* connection.kbytes-per-second */
1181 break;
1182 case 20:{/* mimetype.assign */
1183 /* translate "application/javascript" to "text/javascript" */
1184 data_string * const ds = (data_string *)
1185 array_get_data_unset(cpv->v.a, CONST_STR_LEN(".js"));
1186 if (NULL != ds /*(note: this does not catch w/ ";charset=...")*/
1187 && buffer_eq_slen(&ds->value,
1188 CONST_STR_LEN("application/javascript")))
1189 buffer_copy_string_len(&ds->value,
1190 CONST_STR_LEN("text/javascript"));
1191 break;
1192 }
1193 case 21:/* mimetype.use-xattr */
1194 case 22:/* etag.use-inode */
1195 case 23:/* etag.use-mtime */
1196 case 24:/* etag.use-size */
1197 case 25:/* debug.log-condition-handling */
1198 case 26:/* debug.log-file-not-found */
1199 case 27:/* debug.log-request-handling */
1200 case 28:/* debug.log-request-header */
1201 case 29:/* debug.log-response-header */
1202 case 30:/* debug.log-timeouts */
1203 case 31:/* debug.log-state-handling */
1204 case 32:/* server.errorlog *//*must match config_log_error_open*/
1205 case 33:/* server.breakagelog */ /* match config_log_error_open*/
1206 break;
1207 default:/* should not happen */
1208 break;
1209 }
1210 }
1211 }
1212
1213 p->defaults.errh = srv->errh;
1214 p->defaults.max_keep_alive_requests = 1000;
1215 p->defaults.max_keep_alive_idle = 5;
1216 p->defaults.max_read_idle = 60;
1217 p->defaults.max_write_idle = 360;
1218 p->defaults.follow_symlink = 1;
1219 p->defaults.allow_http11 = 1;
1220 p->defaults.etag_flags = ETAG_USE_INODE | ETAG_USE_MTIME | ETAG_USE_SIZE;
1221 p->defaults.range_requests = 1;
1222 /* use 2 to detect later if value is set by user config in global section */
1223 p->defaults.force_lowercase_filenames = 2;
1224
1225 /*(global, but store in r->conf.http_parseopts)*/
1226 p->defaults.http_parseopts =
1227 (srv->srvconf.http_header_strict ? HTTP_PARSEOPT_HEADER_STRICT :0)
1228 | (srv->srvconf.http_host_strict ? (HTTP_PARSEOPT_HOST_STRICT
1229 |HTTP_PARSEOPT_HOST_NORMALIZE) :0)
1230 | (srv->srvconf.http_host_normalize ? HTTP_PARSEOPT_HOST_NORMALIZE :0)
1231 | (srv->srvconf.http_method_get_body ? HTTP_PARSEOPT_METHOD_GET_BODY :0);
1232 p->defaults.http_parseopts |= srv->srvconf.http_url_normalize;
1233 p->defaults.mimetypes = &srv->srvconf.empty_array; /*(must not be NULL)*/
1234 p->defaults.h2proto = srv->srvconf.h2proto;
1235
1236 /* initialize p->defaults from global config context */
1237 if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
1238 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
1239 if (-1 != cpv->k_id)
1240 config_merge_config(&p->defaults, cpv);
1241 }
1242
1243 /* (after processing config defaults) */
1244 p->defaults.max_request_field_size = srv->srvconf.max_request_field_size;
1245 p->defaults.log_request_header_on_error =
1246 srv->srvconf.log_request_header_on_error;
1247 if (p->defaults.log_request_handling || p->defaults.log_request_header)
1248 p->defaults.log_request_header_on_error = 1;
1249
1250 request_config_set_defaults(&p->defaults);
1251
1252 return rc;
1253 }
1254
config_finalize(server * srv,const buffer * default_server_tag)1255 int config_finalize(server *srv, const buffer *default_server_tag) {
1256 /* (call after plugins_call_set_defaults()) */
1257
1258 config_data_base * const p = srv->config_data_base;
1259
1260 /* settings might be enabled during plugins_call_set_defaults() */
1261 p->defaults.high_precision_timestamps =
1262 srv->srvconf.high_precision_timestamps =
1263 config_feature_bool(srv, "server.metrics-high-precision",
1264 srv->srvconf.high_precision_timestamps);
1265
1266 /* configure default server_tag if not set
1267 * (if configured to blank, unset server_tag)*/
1268 if (!p->defaults.server_tag)
1269 p->defaults.server_tag = default_server_tag;
1270 else if (buffer_is_blank(p->defaults.server_tag))
1271 p->defaults.server_tag = NULL;
1272
1273 /* dump unused config keys */
1274 for (uint32_t i = 0; i < srv->config_context->used; ++i) {
1275 array *config = ((data_config *)srv->config_context->data[i])->value;
1276 for (uint32_t j = 0; config && j < config->used; ++j) {
1277 const buffer * const k = &config->data[j]->key;
1278
1279 /* all var.* is known as user defined variable */
1280 if (strncmp(k->ptr, "var.", sizeof("var.") - 1) == 0)
1281 continue;
1282 /* mod_dirlisting not loaded if dir-listing.activate not enabled */
1283 if (strncmp(k->ptr, "dir-listing.", sizeof("dir-listing.") - 1) == 0
1284 && strcmp(k->ptr, "dir-listing.activate") != 0)
1285 continue;
1286
1287 if (!array_get_element_klen(srv->srvconf.config_touched,
1288 BUF_PTR_LEN(k)))
1289 log_error(srv->errh, __FILE__, __LINE__,
1290 "WARNING: unknown config-key: %s (ignored)", k->ptr);
1291 }
1292 }
1293
1294 array_free(srv->srvconf.config_touched);
1295 srv->srvconf.config_touched = NULL;
1296
1297 if (srv->srvconf.config_unsupported || srv->srvconf.config_deprecated) {
1298 if (srv->srvconf.config_unsupported)
1299 log_error(srv->errh, __FILE__, __LINE__,
1300 "Configuration contains unsupported keys. Going down.");
1301 if (srv->srvconf.config_deprecated)
1302 log_error(srv->errh, __FILE__, __LINE__,
1303 "Configuration contains deprecated keys. Going down.");
1304 return 0;
1305 }
1306
1307 /* check if condition regex captures are used by modules (redirect,rewrite)
1308 * and convert back to regex if condition was simplified to non-regex by
1309 * configparser_simplify_regex() */
1310 if (__builtin_expect( (srv->config_captures != 0), 0)) {
1311 for (uint32_t i = 1; i < srv->config_context->used; ++i) {
1312 data_config * const dc =
1313 (data_config *)srv->config_context->data[i];
1314 if (__builtin_expect( (0 == dc->capture_idx), 1))
1315 continue;
1316 switch (dc->cond) {
1317 case CONFIG_COND_EQ:
1318 case CONFIG_COND_PREFIX:
1319 case CONFIG_COND_SUFFIX:
1320 break;
1321 /*case CONFIG_COND_NE:*/
1322 /*case CONFIG_COND_MATCH:*/
1323 /*case CONFIG_COND_NOMATCH:*/
1324 /*case CONFIG_COND_ELSE:*/
1325 default:
1326 continue;
1327 }
1328 buffer * const b = &dc->string;
1329 if (dc->cond != CONFIG_COND_SUFFIX || b->ptr[0] == '.') {
1330 buffer_extend(b, 1);
1331 memmove(b->ptr+1, b->ptr, buffer_clen(b)-1);
1332 b->ptr[0] = (dc->cond == CONFIG_COND_SUFFIX) ? '\\' : '^';
1333 }
1334 if (dc->cond != CONFIG_COND_PREFIX)
1335 buffer_append_char(b, '$');
1336 dc->cond = CONFIG_COND_MATCH;
1337 /*(config_pcre_keyvalue())*/
1338 const int pcre_jit = config_feature_bool(srv, "server.pcre_jit", 1);
1339 if (!data_config_pcre_compile(dc, pcre_jit, srv->errh))
1340 return 0;
1341 }
1342 }
1343
1344 #ifdef HAVE_PCRE2_H
1345 for (uint32_t i = 1; i < srv->config_context->used; ++i) {
1346 data_config * const dc =
1347 (data_config *)srv->config_context->data[i];
1348 if ((dc->cond == CONFIG_COND_MATCH || dc->cond == CONFIG_COND_NOMATCH)
1349 && 0 == dc->capture_idx) {
1350 if (__builtin_expect( (NULL == srv->match_data), 0)) {
1351 #if 0
1352 /* calculate max output vector size to save a few bytes;
1353 * currently using hard-coded ovec_max = 10 below
1354 * (increase in code size is probably more than bytes saved) */
1355 uint32_t ovec_max = 0;
1356 for (uint32_t j = i; j < srv->config_context->used; ++j) {
1357 const data_config * const dc =
1358 (data_config *)srv->config_context->data[j];
1359 if ((dc->cond == CONFIG_COND_MATCH
1360 || dc->cond == CONFIG_COND_NOMATCH)
1361 && 0 == dc->capture_idx) {
1362 uint32_t v;
1363 if (0==pcre2_pattern_info(dc->code,
1364 PCRE2_INFO_CAPTURECOUNT,&v)) {
1365 if (ovec_max < v)
1366 ovec_max = v;
1367 }
1368 }
1369 }
1370 #else
1371 uint32_t ovec_max = 10;
1372 #endif
1373 srv->match_data = pcre2_match_data_create(ovec_max, NULL);
1374 force_assert(srv->match_data);
1375 }
1376 dc->match_data = srv->match_data;
1377 }
1378 }
1379 #endif
1380
1381 return 1;
1382 }
1383
1384
1385 /* Save some bytes using buffer_append_string() in cold funcs to print config
1386 * (instead of buffer_append_string_len() w/ CONST_STR_LEN() on constant strs)*/
1387
1388 static void config_print_by_type(const data_unset *du, buffer *b, int depth);
1389
config_print_indent(buffer * b,int depth)1390 static void config_print_indent(buffer *b, int depth) {
1391 depth <<= 2;
1392 memset(buffer_extend(b, depth), ' ', depth);
1393 }
1394
1395 __attribute_pure__
config_print_array_max_klen(const array * const a)1396 static uint32_t config_print_array_max_klen(const array * const a) {
1397 uint32_t maxlen = 0;
1398 for (uint32_t i = 0; i < a->used; ++i) {
1399 uint32_t len = buffer_clen(&a->data[i]->key);
1400 if (maxlen < len)
1401 maxlen = len;
1402 }
1403 return maxlen;
1404 }
1405
config_print_array(const array * const a,buffer * const b,int depth)1406 static void config_print_array(const array * const a, buffer * const b, int depth) {
1407 if (a->used <= 5 && (!a->used || buffer_is_unset(&a->data[0]->key))) {
1408 int oneline = 1;
1409 for (uint32_t i = 0; i < a->used; ++i) {
1410 data_unset *du = a->data[i];
1411 if (du->type != TYPE_STRING && du->type != TYPE_INTEGER) {
1412 oneline = 0;
1413 break;
1414 }
1415 }
1416 if (oneline) {
1417 buffer_append_string(b, "(");
1418 for (uint32_t i = 0; i < a->used; ++i) {
1419 if (i != 0)
1420 buffer_append_string(b, ", ");
1421 config_print_by_type(a->data[i], b, depth + 1);
1422 }
1423 buffer_append_string(b, ")");
1424 return;
1425 }
1426 }
1427
1428 const uint32_t maxlen = config_print_array_max_klen(a);
1429 buffer_append_string(b, "(\n");
1430 for (uint32_t i = 0; i < a->used; ++i) {
1431 config_print_indent(b, depth + 1);
1432 data_unset *du = a->data[i];
1433 if (!buffer_is_unset(&du->key)) {
1434 buffer_append_str3(b, CONST_STR_LEN("\""),
1435 BUF_PTR_LEN(&du->key),
1436 CONST_STR_LEN("\""));
1437 int indent = (int)(maxlen - buffer_clen(&du->key));
1438 if (indent > 0)
1439 memset(buffer_extend(b, indent), ' ', indent);
1440 buffer_append_string(b, " => ");
1441 }
1442 config_print_by_type(du, b, depth + 1);
1443 buffer_append_string(b, ",\n");
1444 }
1445 config_print_indent(b, depth);
1446 buffer_append_string(b, ")");
1447 }
1448
config_print_config(const data_unset * d,buffer * const b,int depth)1449 static void config_print_config(const data_unset *d, buffer * const b, int depth) {
1450 data_config *dc = (data_config *)d;
1451 array *a = (array *)dc->value;
1452
1453 if (0 == dc->context_ndx) {
1454 buffer_append_string(b, "config {\n");
1455 }
1456 else {
1457 if (dc->cond != CONFIG_COND_ELSE) {
1458 buffer_append_string(b, dc->comp_key);
1459 buffer_append_string(b, " ");
1460 }
1461 buffer_append_string(b, "{\n");
1462 config_print_indent(b, depth + 1);
1463 buffer_append_string(b, "# block ");
1464 buffer_append_int(b, dc->context_ndx);
1465 buffer_append_string(b, "\n");
1466 }
1467 ++depth;
1468
1469 const uint32_t maxlen = config_print_array_max_klen(a);
1470 for (uint32_t i = 0; i < a->used; ++i) {
1471 config_print_indent(b, depth);
1472 data_unset *du = a->data[i];
1473 buffer_append_string_buffer(b, &du->key);
1474 int indent = (int)(maxlen - buffer_clen(&du->key));
1475 if (indent > 0)
1476 memset(buffer_extend(b, indent), ' ', indent);
1477 buffer_append_string(b, " = ");
1478 config_print_by_type(du, b, depth);
1479 buffer_append_string(b, "\n");
1480 }
1481
1482 buffer_append_string(b, "\n");
1483 for (uint32_t i = 0; i < dc->children.used; ++i) {
1484 data_config *dcc = dc->children.data[i];
1485
1486 /* only the 1st block of chaining */
1487 if (NULL == dcc->prev) {
1488 buffer_append_string(b, "\n");
1489 config_print_indent(b, depth);
1490 config_print_by_type((data_unset *) dcc, b, depth);
1491 buffer_append_string(b, "\n");
1492 }
1493 }
1494
1495 --depth;
1496 config_print_indent(b, depth);
1497 buffer_append_string(b, "}");
1498 if (0 != dc->context_ndx) {
1499 buffer_append_string(b, " # end of ");
1500 buffer_append_string(b, (dc->cond != CONFIG_COND_ELSE)
1501 ? dc->comp_key
1502 : "else");
1503 }
1504
1505 if (dc->next) {
1506 buffer_append_string(b, "\n");
1507 config_print_indent(b, depth);
1508 buffer_append_string(b, "else ");
1509 config_print_by_type((data_unset *)dc->next, b, depth);
1510 }
1511 }
1512
config_print_string(const data_unset * du,buffer * const b)1513 static void config_print_string(const data_unset *du, buffer * const b) {
1514 /* print out the string as is, except prepend '"' with backslash */
1515 const buffer * const vb = &((data_string *)du)->value;
1516 char *dst = buffer_string_prepare_append(b, buffer_clen(vb)*2);
1517 uint32_t n = 0;
1518 dst[n++] = '"';
1519 if (vb->ptr) {
1520 for (const char *p = vb->ptr; *p; ++p) {
1521 if (*p == '"')
1522 dst[n++] = '\\';
1523 dst[n++] = *p;
1524 }
1525 }
1526 dst[n++] = '"';
1527 buffer_commit(b, n);
1528 }
1529
1530 __attribute_cold__
config_print_by_type(const data_unset * const du,buffer * const b,int depth)1531 static void config_print_by_type(const data_unset * const du, buffer * const b, int depth) {
1532 switch (du->type) {
1533 case TYPE_STRING:
1534 config_print_string(du, b);
1535 break;
1536 case TYPE_INTEGER:
1537 buffer_append_int(b, ((data_integer *)du)->value);
1538 break;
1539 case TYPE_ARRAY:
1540 config_print_array(&((data_array *)du)->value, b, depth);
1541 break;
1542 case TYPE_CONFIG:
1543 config_print_config(du, b, depth);
1544 break;
1545 default:
1546 /*if (du->fn->print) du->fn->print(du, b, depth);*/
1547 break;
1548 }
1549 }
1550
config_print(server * srv)1551 void config_print(server *srv) {
1552 buffer_clear(srv->tmp_buf);
1553 data_unset *dc = srv->config_context->data[0];
1554 config_print_by_type(dc, srv->tmp_buf, 0);
1555 }
1556
config_free(server * srv)1557 void config_free(server *srv) {
1558 /*request_config_set_defaults(NULL);*//*(not necessary)*/
1559 config_free_config(srv->config_data_base);
1560
1561 array_free(srv->config_context);
1562 array_free(srv->srvconf.config_touched);
1563 array_free(srv->srvconf.modules);
1564 array_free(srv->srvconf.upload_tempdirs);
1565 #ifdef HAVE_PCRE2_H
1566 if (NULL == srv->match_data) pcre2_match_data_free(srv->match_data);
1567 #endif
1568 }
1569
config_init(server * srv)1570 void config_init(server *srv) {
1571 srv->config_context = array_init(16);
1572 srv->srvconf.config_touched = array_init(128);
1573
1574 srv->srvconf.port = 0;
1575 srv->srvconf.dont_daemonize = 0;
1576 srv->srvconf.preflight_check = 0;
1577 srv->srvconf.compat_module_load = 1;
1578 srv->srvconf.systemd_socket_activation = 0;
1579
1580 srv->srvconf.high_precision_timestamps = 0;
1581 srv->srvconf.max_request_field_size = 8192;
1582
1583 srv->srvconf.http_header_strict = 1;
1584 srv->srvconf.http_host_strict = 1; /*(implies http_host_normalize)*/
1585 srv->srvconf.http_host_normalize = 0;
1586 srv->srvconf.http_url_normalize =
1587 HTTP_PARSEOPT_URL_NORMALIZE
1588 | HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED
1589 | HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT
1590 | HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE
1591 | HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE
1592 | HTTP_PARSEOPT_URL_NORMALIZE_INVALID_UTF8_REJECT;
1593
1594 srv->srvconf.modules = array_init(16);
1595 srv->srvconf.modules_dir = LIBRARY_DIR;
1596 srv->srvconf.upload_tempdirs = array_init(2);
1597 }
1598
1599 /**
1600 * open the errorlog
1601 *
1602 * we have 4 possibilities:
1603 * - stderr (default)
1604 * - syslog
1605 * - logfile
1606 * - pipe
1607 *
1608 */
1609
config_log_error_open_syslog(server * srv,log_error_st * errh,const buffer * syslog_facility)1610 static void config_log_error_open_syslog(server *srv, log_error_st *errh, const buffer *syslog_facility) {
1611 #ifdef HAVE_SYSLOG_H
1612 /*assert(errh->mode == FDLOG_FD);*/
1613 /*assert(errh->fd == STDERR_FILENO);*/
1614 errh->mode = FDLOG_SYSLOG;
1615 errh->fd = -1;
1616 /* perhaps someone wants to use syslog() */
1617 int facility = -1;
1618 if (syslog_facility) {
1619 static const struct facility_name_st {
1620 const char *name;
1621 int val;
1622 } facility_names[] = {
1623 { "auth", LOG_AUTH }
1624 #ifdef LOG_AUTHPRIV
1625 ,{ "authpriv", LOG_AUTHPRIV }
1626 #endif
1627 #ifdef LOG_CRON
1628 ,{ "cron", LOG_CRON }
1629 #endif
1630 ,{ "daemon", LOG_DAEMON }
1631 #ifdef LOG_FTP
1632 ,{ "ftp", LOG_FTP }
1633 #endif
1634 #ifdef LOG_KERN
1635 ,{ "kern", LOG_KERN }
1636 #endif
1637 #ifdef LOG_LPR
1638 ,{ "lpr", LOG_LPR }
1639 #endif
1640 #ifdef LOG_MAIL
1641 ,{ "mail", LOG_MAIL }
1642 #endif
1643 #ifdef LOG_NEWS
1644 ,{ "news", LOG_NEWS }
1645 #endif
1646 ,{ "security", LOG_AUTH } /* DEPRECATED */
1647 #ifdef LOG_SYSLOG
1648 ,{ "syslog", LOG_SYSLOG }
1649 #endif
1650 #ifdef LOG_USER
1651 ,{ "user", LOG_USER }
1652 #endif
1653 #ifdef LOG_UUCP
1654 ,{ "uucp", LOG_UUCP }
1655 #endif
1656 ,{ "local0", LOG_LOCAL0 }
1657 ,{ "local1", LOG_LOCAL1 }
1658 ,{ "local2", LOG_LOCAL2 }
1659 ,{ "local3", LOG_LOCAL3 }
1660 ,{ "local4", LOG_LOCAL4 }
1661 ,{ "local5", LOG_LOCAL5 }
1662 ,{ "local6", LOG_LOCAL6 }
1663 ,{ "local7", LOG_LOCAL7 }
1664 };
1665 for (unsigned int i = 0; i < sizeof(facility_names)/sizeof(facility_names[0]); ++i) {
1666 const struct facility_name_st *f = facility_names+i;
1667 if (0 == strcmp(syslog_facility->ptr, f->name)) {
1668 facility = f->val;
1669 break;
1670 }
1671 }
1672 if (-1 == facility) {
1673 log_error(srv->errh, __FILE__, __LINE__,
1674 "unrecognized server.syslog-facility: \"%s\"; "
1675 "defaulting to \"daemon\" facility",
1676 syslog_facility->ptr);
1677 }
1678 }
1679 openlog("lighttpd", LOG_CONS|LOG_PID, -1==facility ? LOG_DAEMON : facility);
1680 #endif
1681 }
1682
config_log_error_open(server * srv)1683 int config_log_error_open(server *srv) {
1684 /* logs are opened after preflight check (srv->srvconf.preflight_check)
1685 * and after dropping privileges instead of being opened during config
1686 * processing */
1687 #ifdef __clang_analyzer__
1688 force_assert(srv->errh);
1689 #endif
1690
1691 config_data_base * const p = srv->config_data_base;
1692 log_error_st *serrh = NULL;
1693
1694 /* future: might be slightly faster to have allocated array of open files
1695 * rather than walking config, but only might matter with many directives */
1696 /* (init i to 0 if global context; to 1 to skip empty global context) */
1697 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
1698 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
1699 for (; -1 != cpv->k_id; ++cpv) {
1700 const char *fn = NULL;
1701 log_error_st *errh = NULL;
1702 switch (cpv->k_id) {
1703 /* NB: these indexes are repeated below switch() block
1704 * and all must stay in sync with configfile.c */
1705 case 32:/* server.errorlog */
1706 if (0 == i) {
1707 if (srv->srvconf.errorlog_use_syslog) continue;
1708 errh = srv->errh;
1709 }
1710 __attribute_fallthrough__
1711 case 33:/* server.breakagelog */
1712 if (!buffer_is_blank(cpv->v.b)) fn = cpv->v.b->ptr;
1713 break;
1714 default:
1715 break;
1716 }
1717
1718 if (NULL == fn) continue;
1719
1720 fdlog_st * const fdlog = fdlog_open(fn);
1721 if (NULL == fdlog) {
1722 log_perror(srv->errh, __FILE__, __LINE__,
1723 "opening errorlog '%s' failed", fn);
1724 return -1;
1725 }
1726
1727 if (errh) {
1728 /*(logfiles are opened early in setup; this function is called
1729 * prior to set_defaults hook, and modules should not save a
1730 * pointer to srv->errh until set_defaults hook or later)*/
1731 p->defaults.errh = srv->errh = fdlog;
1732 log_set_global_errh(srv->errh, 0);
1733 }
1734 cpv->v.v = errh = fdlog;
1735 cpv->vtype = T_CONFIG_LOCAL;
1736
1737 if (0 == i && errh != srv->errh) /*(top-level server.breakagelog)*/
1738 serrh = errh;
1739 }
1740 }
1741
1742 if (config_feature_bool(srv, "server.errorlog-high-precision", 0))
1743 log_set_global_errh(srv->errh, 1);
1744
1745 if (srv->srvconf.errorlog_use_syslog) /*(restricted to global scope)*/
1746 config_log_error_open_syslog(srv, srv->errh,
1747 srv->srvconf.syslog_facility);
1748 else if (srv->errh->mode == FDLOG_FD && !srv->srvconf.dont_daemonize)
1749 srv->errh->fd = -1;
1750 /* We can only log to stderr in dont-daemonize mode;
1751 * if we do daemonize and no errorlog file is specified,
1752 * we log into /dev/null
1753 */
1754
1755 /* Note: serrh should not be stored in p->defaults.serrh
1756 * If left as NULL, scripts (e.g. mod_cgi and mod_ssi exec) will inherit
1757 * the current STDERR_FILENO, which already is the top-level breakagelog. */
1758 /*p->defaults.serrh = serrh;*/
1759
1760 int errfd;
1761 if (NULL != serrh) {
1762 if (srv->errh->mode == FDLOG_FD) {
1763 srv->errh->fd = dup(STDERR_FILENO);
1764 fdevent_setfd_cloexec(srv->errh->fd);
1765 }
1766
1767 errfd = serrh->fd;
1768 if (*serrh->fn == '|') fdlog_pipe_serrh(errfd); /* breakagelog */
1769 }
1770 else if (!srv->srvconf.dont_daemonize) {
1771 /* move STDERR_FILENO to /dev/null */
1772 if (-1 == (errfd = fdevent_open_devnull())) {
1773 log_perror(srv->errh,__FILE__,__LINE__,"opening /dev/null failed");
1774 return -1;
1775 }
1776 }
1777 else {
1778 /*(leave STDERR_FILENO as-is)*/
1779 errfd = -1;
1780 }
1781
1782 if (0 != fdevent_set_stdin_stdout_stderr(-1, -1, errfd)) {
1783 log_perror(srv->errh, __FILE__, __LINE__, "setting stderr failed");
1784 #ifdef FD_CLOEXEC
1785 if (-1 != errfd && NULL == serrh) close(errfd);
1786 #endif
1787 return -1;
1788 }
1789 #ifdef FD_CLOEXEC
1790 if (-1 != errfd && NULL == serrh) close(errfd);
1791 #endif
1792
1793 if (NULL != serrh) {
1794 close(errfd); /* serrh->fd */
1795 serrh->fd = STDERR_FILENO;
1796 }
1797
1798 return 0;
1799 }
1800
config_log_error_close(server * srv)1801 void config_log_error_close(server *srv) {
1802 config_data_base * const p = srv->config_data_base;
1803 if (NULL == p) return;
1804
1805 /*(reset serrh just in case; should not be used after this func returns)*/
1806 p->defaults.serrh = NULL;
1807
1808 fdlog_closeall(srv->errh); /*(close all except srv->errh)*/
1809
1810 if (srv->errh->mode == FDLOG_SYSLOG) {
1811 srv->errh->mode = FDLOG_FD;
1812 srv->errh->fd = STDERR_FILENO;
1813 #ifdef HAVE_SYSLOG_H
1814 closelog();
1815 #endif
1816 }
1817 }
1818
1819
1820
1821 typedef struct {
1822 const char *source;
1823 const char *input;
1824 int offset;
1825 int size;
1826
1827 int line_pos;
1828 int line;
1829
1830 int in_key;
1831 int parens;
1832 int in_cond;
1833 int simulate_eol;
1834 log_error_st *errh;
1835 } tokenizer_t;
1836
1837 __attribute_pure__
config_skip_newline(const tokenizer_t * const t)1838 static int config_skip_newline(const tokenizer_t * const t) {
1839 const char * const s = t->input + t->offset;
1840 /*force_assert(s[0] == '\r' || s[0] == '\n');*/
1841 return 1 + (s[0] == '\r' && s[1] == '\n');
1842 }
1843
1844 __attribute_noinline__
1845 __attribute_pure__
config_skip_comment(const tokenizer_t * const t)1846 static int config_skip_comment(const tokenizer_t * const t) {
1847 /*assert(t->input[t->offset] == '#');*/
1848 const char *s = t->input + t->offset;
1849 do { ++s; } while (*s && *s != '\r' && *s != '\n');
1850 return (int)(s - t->input);
1851 }
1852
1853 __attribute_cold__
config_tokenizer_err(tokenizer_t * t,const char * file,unsigned int line,const char * msg)1854 static int config_tokenizer_err(tokenizer_t *t, const char *file, unsigned int line, const char *msg) {
1855 log_error(t->errh, file, line, "source: %s line: %d pos: %d %s",
1856 t->source, t->line, t->offset - t->line_pos, msg);
1857 return -1;
1858 }
1859
config_tokenizer(tokenizer_t * t,buffer * token)1860 static int config_tokenizer(tokenizer_t *t, buffer *token) {
1861 if (t->simulate_eol) {
1862 t->simulate_eol = 0;
1863 t->in_key = 1;
1864 buffer_copy_string_len(token, CONST_STR_LEN("(EOL)"));
1865 return TK_EOL;
1866 }
1867
1868 while (t->offset < t->size) {
1869 const char * const s = t->input + t->offset;
1870 switch (s[0]) {
1871 case '\t':
1872 case ' ':
1873 t->offset++;
1874 break;
1875 case '=':
1876 if (t->parens) {
1877 if (s[1] != '>')
1878 return config_tokenizer_err(t, __FILE__, __LINE__,
1879 "use => for assignments in arrays");
1880 t->offset += 2;
1881 buffer_copy_string_len(token, s, 2); /* "=>" */
1882 return TK_ARRAY_ASSIGN;
1883 }
1884 else if (t->in_cond) {
1885 int tid;
1886 switch (s[1]) {
1887 case '=': tid = TK_EQ; break;
1888 case '~': tid = TK_MATCH; break;
1889 case '^': tid = TK_PREFIX; break;
1890 case '$': tid = TK_SUFFIX; break;
1891 default:
1892 return config_tokenizer_err(t, __FILE__, __LINE__,
1893 "only == =~ =^ =$ are allowed in the condition");
1894 }
1895 t->offset += 2;
1896 t->in_key = 1;
1897 t->in_cond = 0;
1898 buffer_copy_string_len(token, s, 2); /* "==" "=~" "=^" "=$" */
1899 return tid;
1900 }
1901 else if (t->in_key) {
1902 t->offset++;
1903 buffer_copy_string_len(token, s, 1); /* "=" */
1904 return TK_ASSIGN;
1905 }
1906 else
1907 return config_tokenizer_err(t, __FILE__, __LINE__,
1908 "unexpected equal-sign: =");
1909 case '!':
1910 if (t->in_cond) {
1911 int tid;
1912 switch (s[1]) {
1913 case '=': tid = TK_NE; break;
1914 case '~': tid = TK_NOMATCH; break;
1915 default:
1916 return config_tokenizer_err(t, __FILE__, __LINE__,
1917 "only !~ and != are allowed in the condition");
1918 }
1919 t->offset += 2;
1920 t->in_key = 1;
1921 t->in_cond = 0;
1922 buffer_copy_string_len(token, s, 2); /* "!=" "!~" */
1923 return tid;
1924 }
1925 else
1926 return config_tokenizer_err(t, __FILE__, __LINE__,
1927 "unexpected exclamation-marks: !");
1928 case '\n':
1929 case '\r':
1930 do {
1931 switch (t->input[t->offset]) {
1932 case '\r':
1933 case '\n':
1934 t->offset += config_skip_newline(t);
1935 t->line_pos = t->offset;
1936 t->line++;
1937 continue;
1938 case '#':
1939 t->offset = config_skip_comment(t);
1940 continue;
1941 case '\t':
1942 case ' ':
1943 t->offset++;
1944 continue;
1945 default:
1946 break;
1947 }
1948 break;
1949 } while (t->offset < t->size);
1950 if (!t->parens) {
1951 t->in_key = 1;
1952 buffer_copy_string_len(token, CONST_STR_LEN("(EOL)"));
1953 return TK_EOL;
1954 }
1955 break;
1956 case ',':
1957 t->offset++;
1958 if (t->parens) {
1959 buffer_copy_string_len(token, CONST_STR_LEN("(COMMA)"));
1960 return TK_COMMA;
1961 }
1962 break;
1963 case '"':
1964 {
1965 /* search for the terminating " */
1966 const char *start = s + 1; /*buffer_blank(token);*/
1967 buffer_copy_string_len(token, CONST_STR_LEN(""));
1968
1969 int i;
1970 for (i = 1; s[i] && s[i] != '"'; ++i) {
1971 if (s[i] == '\\' && s[i+1] == '"') {
1972 buffer_append_string_len(token, start, s + i - start);
1973 start = s + ++i; /* step over '"' */
1974 }
1975 }
1976
1977 if (s[i] == '\0') {
1978 return config_tokenizer_err(t, __FILE__, __LINE__,
1979 "missing closing quote");
1980 }
1981
1982 t->offset += i + 1;
1983 buffer_append_string_len(token, start, s + i - start);
1984 return TK_STRING;
1985 }
1986 case '(':
1987 t->offset++;
1988 t->parens++;
1989 buffer_copy_string_len(token, s, 1); /* "(" */
1990 return TK_LPARAN;
1991 case ')':
1992 if (!t->parens)
1993 return config_tokenizer_err(t, __FILE__, __LINE__,
1994 "close-parens seen open-parens");
1995 t->offset++;
1996 t->parens--;
1997 buffer_copy_string_len(token, s, 1); /* ")" */
1998 return TK_RPARAN;
1999 case '$':
2000 t->offset++;
2001 t->in_cond = 1;
2002 t->in_key = 0;
2003 buffer_copy_string_len(token, s, 1); /* "$" */
2004 return TK_DOLLAR;
2005 case '+':
2006 if (s[1] == '=') {
2007 t->offset += 2;
2008 buffer_copy_string_len(token, s, 2); /* "+=" */
2009 return TK_APPEND;
2010 }
2011 else {
2012 t->offset++;
2013 buffer_copy_string_len(token, s, 1); /* "+" */
2014 return TK_PLUS;
2015 }
2016 case ':':
2017 if (s[1] != '=')
2018 return config_tokenizer_err(t, __FILE__, __LINE__,
2019 "unexpected character ':'");
2020 t->offset += 2;
2021 buffer_copy_string_len(token, s, 2); /* ":=" */
2022 return TK_FORCE_ASSIGN;
2023 case '{':
2024 t->offset++;
2025 buffer_copy_string_len(token, s, 1); /* "{" */
2026 return TK_LCURLY;
2027 case '}':
2028 while (++t->offset < t->size) {
2029 int c = t->input[t->offset];
2030 if (c == '\r' || c == '\n') {
2031 break;
2032 }
2033 else if (c == '#') {
2034 t->offset = config_skip_comment(t);
2035 break;
2036 }
2037 else if (c != ' ' && c != '\t') {
2038 t->simulate_eol = 1;
2039 break;
2040 } /* else (c == ' ' || c == '\t') */
2041 }
2042 buffer_copy_string_len(token, s, 1); /* "}" */
2043 return TK_RCURLY;
2044 case '[':
2045 t->offset++;
2046 buffer_copy_string_len(token, s, 1); /* "[" */
2047 return TK_LBRACKET;
2048 case ']':
2049 t->offset++;
2050 buffer_copy_string_len(token, s, 1); /* "]" */
2051 return TK_RBRACKET;
2052 case '#':
2053 t->offset = config_skip_comment(t);
2054 break;
2055 case '\0':
2056 config_tokenizer_err(t, __FILE__, __LINE__, "stray NUL");
2057 return 0;
2058 default:
2059 if (t->in_cond) {
2060 int i = 0;
2061 while (light_isalpha(s[i]) || s[i] == '_') ++i;
2062 if (i && s[i]) {
2063 t->offset += i;
2064 buffer_copy_string_len(token, s, i);
2065 return TK_SRVVARNAME;
2066 }
2067 else
2068 return config_tokenizer_err(t, __FILE__, __LINE__,
2069 "invalid character in condition");
2070 }
2071 else if (light_isdigit(s[0])) {
2072 /* take all digits */
2073 int i = 1;
2074 while (light_isdigit(s[i])) ++i;
2075 t->offset += i;
2076 buffer_copy_string_len(token, s, i);
2077 return TK_INTEGER;
2078 }
2079 else {
2080 /* the key might consist of [-.0-9a-z] */
2081 int i = 0;
2082 while (light_isalnum(s[i])
2083 || s[i] == '.'
2084 || s[i] == '_' /* for env.* */
2085 || s[i] == '-') ++i;
2086
2087 if (i && s[i]) {
2088 t->offset += i;
2089 buffer_copy_string_len(token, s, i);
2090 if (0 == strcmp(token->ptr, "include"))
2091 return TK_INCLUDE;
2092 else if (0 == strcmp(token->ptr, "include_shell"))
2093 return TK_INCLUDE_SHELL;
2094 else if (0 == strcmp(token->ptr, "global"))
2095 return TK_GLOBAL;
2096 else if (0 == strcmp(token->ptr, "else"))
2097 return TK_ELSE;
2098 else
2099 return TK_LKEY;
2100 }
2101 else if (0 == i
2102 && ((uint8_t *)s)[0] == 0xc2
2103 && ((uint8_t *)s)[1] == 0xa0) {
2104 /* treat U+00A0 (c2 a0) "NO-BREAK SPACE" as whitespace */
2105 /* http://www.fileformat.info/info/unicode/char/a0/index.htm */
2106 t->offset+=2;
2107 }
2108 else
2109 return config_tokenizer_err(t, __FILE__, __LINE__,
2110 "invalid character in variable name");
2111 }
2112 break;
2113 }
2114 }
2115 return 0;
2116 }
2117
config_parse(server * srv,config_t * context,const char * source,const char * input,int isize)2118 static int config_parse(server *srv, config_t *context, const char *source, const char *input, int isize) {
2119 tokenizer_t t;
2120 buffer * const lasttoken = buffer_init();
2121 int ret;
2122
2123 t.source = source;
2124 t.input = input;
2125 t.size = isize;
2126 t.offset = 0;
2127 t.line = 1;
2128 t.line_pos = 0;
2129
2130 t.in_key = 1;
2131 t.parens = 0;
2132 t.in_cond = 0;
2133 t.simulate_eol = 0;
2134 t.errh = srv->errh;
2135
2136 void * const pParser = configparserAlloc( malloc );
2137 force_assert(pParser);
2138 do {
2139 ret = config_tokenizer(&t, lasttoken);
2140 if (__builtin_expect( (ret <= 0), 0))
2141 break;
2142 buffer * const token = buffer_init();
2143 buffer_copy_buffer(token, lasttoken);
2144 configparser(pParser, ret, token, context);
2145 /*token = NULL;*/
2146 } while (context->ok);
2147
2148 if (ret != -1 && context->ok) {
2149 /* add an EOL at EOF, better than say sorry */
2150 buffer * const token = buffer_init();
2151 buffer_copy_string(token, "(EOL)");
2152 configparser(pParser, TK_EOL, token, context);
2153 /*token = NULL;*/
2154 if (context->ok) {
2155 configparser(pParser, 0, NULL, context);
2156 }
2157 }
2158 configparserFree(pParser, free);
2159
2160 if (ret == -1) {
2161 log_error(t.errh, __FILE__, __LINE__,
2162 "configfile parser failed at: %s", lasttoken->ptr);
2163 } else if (context->ok == 0) {
2164 log_error(t.errh, __FILE__, __LINE__, "source: %s line: %d pos: %d "
2165 "parser failed somehow near here: %s",
2166 t.source, t.line, t.offset - t.line_pos, lasttoken->ptr);
2167 ret = -1;
2168 }
2169 buffer_free(lasttoken);
2170
2171 return ret == -1 ? -1 : 0;
2172 }
2173
2174 __attribute_cold__
config_parse_stdin(server * srv,config_t * context)2175 static int config_parse_stdin(server *srv, config_t *context) {
2176 buffer * const b = chunk_buffer_acquire();
2177 size_t dlen;
2178 ssize_t n = -1;
2179 do {
2180 if (n > 0)
2181 buffer_commit(b, n);
2182 size_t avail = buffer_string_space(b);
2183 dlen = buffer_clen(b);
2184 if (__builtin_expect( (avail < 1024), 0)) {
2185 /*(arbitrary limit: 32 MB file; expect < 1 MB)*/
2186 if (dlen >= 32*1024*1024) {
2187 log_error(srv->errh, __FILE__, __LINE__,
2188 "config read from stdin is way too large");
2189 break;
2190 }
2191 avail = chunk_buffer_prepare_append(b, b->size+avail);
2192 }
2193 n = read(STDIN_FILENO, b->ptr+dlen, avail);
2194 } while (n > 0 || (n == -1 && errno == EINTR));
2195 int rc = -1;
2196 if (0 == n)
2197 rc = dlen ? config_parse(srv, context, "-", b->ptr, (int)dlen) : 0;
2198 else
2199 log_perror(srv->errh, __FILE__, __LINE__, "config read from stdin");
2200
2201 if (dlen)
2202 ck_memzero(b->ptr, dlen);
2203 chunk_buffer_release(b);
2204 return rc;
2205 }
2206
config_parse_file_stream(server * srv,config_t * context,const char * fn)2207 static int config_parse_file_stream(server *srv, config_t *context, const char *fn) {
2208 off_t dlen = 32*1024*1024;/*(arbitrary limit: 32 MB file; expect < 1 MB)*/
2209 char *data = fdevent_load_file(fn, &dlen, NULL, malloc, free);
2210 if (NULL == data) {
2211 log_perror(srv->errh, __FILE__, __LINE__,
2212 "opening configfile %s failed", fn);
2213 return -1;
2214 }
2215
2216 int rc = 0;
2217 if (dlen) {
2218 rc = config_parse(srv, context, fn, data, (int)dlen);
2219 ck_memzero(data, (size_t)dlen);
2220 }
2221 free(data);
2222 return rc;
2223 }
2224
config_parse_file(server * srv,config_t * context,const char * fn)2225 int config_parse_file(server *srv, config_t *context, const char *fn) {
2226 buffer * const filename = buffer_init();
2227 const size_t fnlen = strlen(fn);
2228 int ret = -1;
2229 #ifdef GLOB_BRACE
2230 int flags = GLOB_BRACE;
2231 #else
2232 int flags = 0;
2233 #endif
2234 glob_t gl;
2235
2236 if (buffer_is_blank(context->basedir) ||
2237 (fn[0] == '/' || fn[0] == '\\') ||
2238 (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\')) ||
2239 (fn[0] == '.' && fn[1] == '.' && (fn[2] == '/' || fn[2] == '\\'))) {
2240 buffer_copy_string_len(filename, fn, fnlen);
2241 } else {
2242 buffer_copy_path_len2(filename, BUF_PTR_LEN(context->basedir),
2243 fn, fnlen);
2244 }
2245
2246 switch (glob(filename->ptr, flags, NULL, &gl)) {
2247 case 0:
2248 for (size_t i = 0; i < gl.gl_pathc; ++i) {
2249 ret = config_parse_file_stream(srv, context, gl.gl_pathv[i]);
2250 if (0 != ret) break;
2251 }
2252 globfree(&gl);
2253 break;
2254 case GLOB_NOMATCH:
2255 if (filename->ptr[strcspn(filename->ptr, "*?[]{}")] != '\0') { /*(contains glob metachars)*/
2256 ret = 0; /* not an error if no files match glob pattern */
2257 }
2258 else {
2259 log_error(srv->errh, __FILE__, __LINE__, "include file not found: %s", filename->ptr);
2260 }
2261 break;
2262 case GLOB_ABORTED:
2263 case GLOB_NOSPACE:
2264 log_perror(srv->errh, __FILE__, __LINE__, "glob() %s failed", filename->ptr);
2265 break;
2266 }
2267
2268 buffer_free(filename);
2269 return ret;
2270 }
2271
2272 #ifdef __CYGWIN__
2273
getCWD(char * buf,size_t sz)2274 static char* getCWD(char *buf, size_t sz) {
2275 if (NULL == getcwd(buf, sz)) {
2276 return NULL;
2277 }
2278 for (size_t i = 0; buf[i]; ++i) {
2279 if (buf[i] == '\\') buf[i] = '/';
2280 }
2281 return buf;
2282 }
2283
2284 #define getcwd(buf, sz) getCWD((buf),(sz))
2285
2286 #endif /* __CYGWIN__ */
2287
config_parse_cmd(server * srv,config_t * context,const char * cmd)2288 int config_parse_cmd(server *srv, config_t *context, const char *cmd) {
2289 int ret = 0;
2290 int fds[2];
2291 char oldpwd[PATH_MAX];
2292
2293 if (NULL == getcwd(oldpwd, sizeof(oldpwd))) {
2294 log_perror(srv->errh, __FILE__, __LINE__, "getcwd()");
2295 return -1;
2296 }
2297
2298 if (!buffer_is_blank(context->basedir)) {
2299 if (0 != chdir(context->basedir->ptr)) {
2300 log_perror(srv->errh, __FILE__, __LINE__,
2301 "cannot change directory to %s", context->basedir->ptr);
2302 return -1;
2303 }
2304 }
2305
2306 if (fdevent_pipe_cloexec(fds, 65536)) {
2307 log_perror(srv->errh, __FILE__, __LINE__, "pipe()");
2308 ret = -1;
2309 }
2310 else {
2311 char *shell = getenv("SHELL");
2312 char *args[4];
2313 pid_t pid;
2314 if (shell && (0 == strcmp(shell, "/usr/bin/false")
2315 || 0 == strcmp(shell, "/bin/false")))
2316 shell = NULL;
2317 *(const char **)&args[0] = shell ? shell : "/bin/sh";
2318 *(const char **)&args[1] = "-c";
2319 *(const char **)&args[2] = cmd;
2320 args[3] = NULL;
2321
2322 pid = fdevent_fork_execve(args[0], args, NULL, -1, fds[1], -1, -1);
2323 if (-1 == pid) {
2324 log_perror(srv->errh, __FILE__, __LINE__, "fork/exec(%s)", cmd);
2325 ret = -1;
2326 }
2327 else {
2328 ssize_t rd;
2329 int wstatus = 0;
2330 buffer *out = buffer_init();
2331 close(fds[1]);
2332 fds[1] = -1;
2333 do {
2334 rd = read(fds[0], buffer_string_prepare_append(out, 1023), 1023);
2335 if (rd >= 0) buffer_commit(out, (size_t)rd);
2336 } while (rd > 0 || (-1 == rd && errno == EINTR));
2337 if (0 != rd) {
2338 log_perror(srv->errh, __FILE__, __LINE__, "read \"%s\"", cmd);
2339 ret = -1;
2340 }
2341 close(fds[0]);
2342 fds[0] = -1;
2343 if (pid != fdevent_waitpid(pid, &wstatus, 0)) {
2344 log_perror(srv->errh, __FILE__, __LINE__, "waitpid \"%s\"",cmd);
2345 ret = -1;
2346 }
2347 if (0 != wstatus) {
2348 log_error(srv->errh, __FILE__, __LINE__,
2349 "command \"%s\" exited non-zero: %d",
2350 cmd, WEXITSTATUS(wstatus));
2351 ret = -1;
2352 }
2353
2354 if (-1 != ret) {
2355 ret = config_parse(srv, context, cmd, BUF_PTR_LEN(out));
2356 }
2357 buffer_free(out);
2358 }
2359 if (-1 != fds[0]) close(fds[0]);
2360 if (-1 != fds[1]) close(fds[1]);
2361 }
2362
2363 if (0 != chdir(oldpwd)) {
2364 log_perror(srv->errh, __FILE__, __LINE__,
2365 "cannot change directory to %s", oldpwd);
2366 ret = -1;
2367 }
2368 return ret;
2369 }
2370
config_remoteip_normalize_ipv6(buffer * const b,buffer * const tb)2371 static int config_remoteip_normalize_ipv6(buffer * const b, buffer * const tb) {
2372 /* $HTTP["remote-ip"] IPv6 accepted with or without '[]' for config compat
2373 * http_request_host_normalize() expects IPv6 with '[]',
2374 * and config processing at runtime expects COMP_HTTP_REMOTE_IP
2375 * compared without '[]', so strip '[]' after normalization */
2376 buffer_clear(tb);
2377 if (b->ptr[0] != '[')
2378 buffer_append_str3(tb,
2379 CONST_STR_LEN("["),
2380 BUF_PTR_LEN(b),
2381 CONST_STR_LEN("]"));
2382 else
2383 buffer_append_string_buffer(tb, b);
2384
2385 int rc = http_request_host_normalize(tb, 0);
2386 if (0 == rc) {
2387 /* remove surrounding '[]' */
2388 size_t blen = buffer_clen(tb);
2389 if (blen > 1) buffer_copy_string_len(b, tb->ptr+1, blen-2);
2390 }
2391 return rc;
2392 }
2393
config_remoteip_normalize(buffer * const b,buffer * const tb)2394 int config_remoteip_normalize(buffer * const b, buffer * const tb) {
2395 if (b->ptr[0] == '/') return 1; /*(skip AF_UNIX /path/file)*/
2396
2397 const char * const slash = strchr(b->ptr, '/'); /* CIDR mask */
2398 const char * const colon = strchr(b->ptr, ':'); /* IPv6 */
2399 unsigned long nm_bits = 0;
2400
2401 if (NULL != slash) {
2402 char *nptr;
2403 nm_bits = strtoul(slash + 1, &nptr, 10);
2404 if (*nptr || 0 == nm_bits || nm_bits > (NULL != colon ? 128 : 32)) {
2405 /*(also rejects (slash+1 == nptr) which results in nm_bits = 0)*/
2406 return -1;
2407 }
2408 buffer_truncate(b, (size_t)(slash - b->ptr));
2409 }
2410
2411 int family = colon ? AF_INET6 : AF_INET;
2412 int rc = (family == AF_INET)
2413 ? http_request_host_normalize(b, 0)
2414 : config_remoteip_normalize_ipv6(b, tb);
2415
2416 uint32_t len = buffer_clen(b); /*(save len before adding CIDR mask)*/
2417 if (nm_bits) {
2418 buffer_append_char(b, '/');
2419 buffer_append_int(b, (int)nm_bits);
2420 }
2421
2422 if (0 != rc) {
2423 return -1;
2424 }
2425
2426 /* extend b to hold structured data after end of string:
2427 * nm_bits and memory-aligned sock_addr for AF_INET or AF_INET6 (28 bytes)*/
2428 char *after = buffer_string_prepare_append(b, 1 + 7 + 28);
2429 ++after; /*(increment to pos after string end '\0')*/
2430 *(unsigned char *)after = (unsigned char)nm_bits;
2431 sock_addr * const addr = (sock_addr *)(((uintptr_t)after+1+7) & ~7);
2432 if (nm_bits) b->ptr[len] = '\0'; /*(sock_addr_inet_pton() w/o CIDR mask)*/
2433 rc = sock_addr_inet_pton(addr, b->ptr, family, 0);
2434 if (nm_bits) b->ptr[len] = '/';
2435 return (1 == rc);
2436 }
2437
2438
context_init(server * srv,config_t * context)2439 static void context_init(server *srv, config_t *context) {
2440 context->srv = srv;
2441 context->ok = 1;
2442 context->configs_stack.data = NULL;
2443 context->configs_stack.used = 0;
2444 context->configs_stack.size = 0;
2445 context->basedir = buffer_init();
2446 }
2447
context_free(config_t * context)2448 static void context_free(config_t *context) {
2449 free(context->configs_stack.data);
2450 buffer_free(context->basedir);
2451 }
2452
2453 __attribute_noinline__
config_vars_init(array * a)2454 static void config_vars_init (array *a) {
2455 char dcwd[PATH_MAX];
2456 if (NULL != getcwd(dcwd, sizeof(dcwd)))
2457 array_set_key_value(a, CONST_STR_LEN("var.CWD"), dcwd, strlen(dcwd));
2458
2459 *array_get_int_ptr(a, CONST_STR_LEN("var.PID")) = getpid();
2460 }
2461
config_read(server * srv,const char * fn)2462 int config_read(server *srv, const char *fn) {
2463 config_t context;
2464 data_config *dc;
2465 int ret;
2466 char *pos;
2467
2468 context_init(srv, &context);
2469 context.all_configs = srv->config_context;
2470
2471 #ifdef __WIN32
2472 pos = strrchr(fn, '\\');
2473 #else
2474 pos = strrchr(fn, '/');
2475 #endif
2476 if (pos) {
2477 buffer_copy_string_len(context.basedir, fn, pos - fn + 1);
2478 }
2479
2480 dc = data_config_init();
2481 buffer_copy_string_len(&dc->key, CONST_STR_LEN("global"));
2482 config_vars_init(dc->value); /* default context */
2483
2484 force_assert(context.all_configs->used == 0);
2485 dc->context_ndx = context.all_configs->used;
2486 array_insert_unique(context.all_configs, (data_unset *)dc);
2487 context.current = dc;
2488
2489 ret = (0 != strcmp(fn, "-")) /*(incompatible with one-shot mode)*/
2490 ? config_parse_file_stream(srv, &context, fn)
2491 : config_parse_stdin(srv, &context);
2492
2493 /* remains nothing if parser is ok */
2494 force_assert(!(0 == ret && context.ok && 0 != context.configs_stack.used));
2495 context_free(&context);
2496
2497 if (0 != ret) {
2498 return ret;
2499 }
2500
2501 /* reorder dc->context_ndx to match srv->config_context->data[] index.
2502 * srv->config_context->data[] may have been re-ordered in configparser.y.
2503 * Since the dc->context_ndx (id) is reused by config_insert*() and by
2504 * plugins to index into srv->config_context->data[], reorder into the
2505 * order encountered during config file parsing for least surprise to
2506 * end-users writing config files. Note: this manipulation *breaks* the
2507 * srv->config_context->sorted[] structure, so searching the array by key
2508 * is no longer valid. */
2509 for (uint32_t i = 0; i < srv->config_context->used; ++i) {
2510 dc = (data_config *)srv->config_context->data[i];
2511 if (dc->context_ndx == (int)i) continue;
2512 for (uint32_t j = i; j < srv->config_context->used; ++j) {
2513 dc = (data_config *)srv->config_context->data[j];
2514 if (dc->context_ndx == (int)i) {
2515 srv->config_context->data[j] = srv->config_context->data[i];
2516 srv->config_context->data[i] = (data_unset *)dc;
2517 break;
2518 }
2519 }
2520 }
2521
2522 if (0 != config_insert_srvconf(srv)) {
2523 return -1;
2524 }
2525
2526 if (0 != config_insert(srv)) {
2527 return -1;
2528 }
2529
2530 return 0;
2531 }
2532
config_set_defaults(server * srv)2533 int config_set_defaults(server *srv) {
2534 size_t i;
2535 request_config *s = &((config_data_base *)srv->config_data_base)->defaults;
2536 struct stat st1, st2;
2537
2538 if (fdevent_config(&srv->srvconf.event_handler, srv->errh) <= 0)
2539 return -1;
2540
2541 if (srv->srvconf.changeroot) {
2542 if (-1 == stat(srv->srvconf.changeroot->ptr, &st1)) {
2543 log_error(srv->errh, __FILE__, __LINE__,
2544 "server.chroot doesn't exist: %s",
2545 srv->srvconf.changeroot->ptr);
2546 return -1;
2547 }
2548 if (!S_ISDIR(st1.st_mode)) {
2549 log_error(srv->errh, __FILE__, __LINE__,
2550 "server.chroot isn't a directory: %s",
2551 srv->srvconf.changeroot->ptr);
2552 return -1;
2553 }
2554 }
2555
2556 if (!srv->srvconf.upload_tempdirs->used) {
2557 const char *tmpdir = getenv("TMPDIR");
2558 if (NULL == tmpdir) tmpdir = "/var/tmp";
2559 array_insert_value(srv->srvconf.upload_tempdirs, tmpdir, strlen(tmpdir));
2560 }
2561
2562 if (srv->srvconf.upload_tempdirs->used) {
2563 buffer * const tb = srv->tmp_buf;
2564 buffer_clear(tb);
2565 if (srv->srvconf.changeroot) {
2566 buffer_copy_buffer(tb, srv->srvconf.changeroot);
2567 }
2568 const size_t len = buffer_clen(tb);
2569
2570 for (i = 0; i < srv->srvconf.upload_tempdirs->used; ++i) {
2571 const data_string * const ds = (data_string *)srv->srvconf.upload_tempdirs->data[i];
2572 if (len) {
2573 buffer_truncate(tb, len);
2574 buffer_append_path_len(tb, BUF_PTR_LEN(&ds->value));
2575 } else {
2576 buffer_copy_buffer(tb, &ds->value);
2577 }
2578 if (-1 == stat(tb->ptr, &st1)) {
2579 log_error(srv->errh, __FILE__, __LINE__,
2580 "server.upload-dirs doesn't exist: %s", tb->ptr);
2581 } else if (!S_ISDIR(st1.st_mode)) {
2582 log_error(srv->errh, __FILE__, __LINE__,
2583 "server.upload-dirs isn't a directory: %s", tb->ptr);
2584 }
2585 }
2586 }
2587
2588 chunkqueue_set_tempdirs_default(
2589 srv->srvconf.upload_tempdirs,
2590 srv->srvconf.upload_temp_file_size);
2591
2592 if (!s->document_root || buffer_is_blank(s->document_root)) {
2593 log_error(srv->errh, __FILE__, __LINE__, "server.document-root is not set");
2594 return -1;
2595 }
2596
2597 if (2 == s->force_lowercase_filenames) { /* user didn't configure it in global section? */
2598 s->force_lowercase_filenames = 0; /* default to 0 */
2599
2600 buffer * const tb = srv->tmp_buf;
2601 buffer_copy_string_len_lc(tb, BUF_PTR_LEN(s->document_root));
2602
2603 if (0 == stat(tb->ptr, &st1)) {
2604 int is_lower = 0;
2605
2606 is_lower = buffer_is_equal(tb, s->document_root);
2607
2608 /* lower-case existed, check upper-case */
2609 buffer_copy_buffer(tb, s->document_root);
2610
2611 buffer_to_upper(tb);
2612
2613 /* we have to handle the special case that upper and lower-casing results in the same filename
2614 * as in server.document-root = "/" or "/12345/" */
2615
2616 if (is_lower && buffer_is_equal(tb, s->document_root)) {
2617 /* lower-casing and upper-casing didn't result in
2618 * another filename, no need to stat(),
2619 * just assume it is case-sensitive. */
2620
2621 s->force_lowercase_filenames = 0;
2622 } else if (0 == stat(tb->ptr, &st2)) {
2623
2624 /* upper case exists too, doesn't the FS handle this ? */
2625
2626 /* upper and lower have the same inode -> case-insensitive FS */
2627
2628 if (st1.st_ino == st2.st_ino) {
2629 /* upper and lower have the same inode -> case-insensitive FS */
2630
2631 s->force_lowercase_filenames = 1;
2632 }
2633 }
2634 }
2635 }
2636
2637 return 0;
2638 }
2639