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