xref: /lighttpd1.4/src/configfile.c (revision 25f5085a)
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