1
2 #include "httpd.h"
3 #include "httpd_policy.h"
4
5 #include <err.h>
6
7 #define EX_UTILS_NO_USE_INIT 1
8 #define EX_UTILS_NO_USE_EXIT 1
9 #define EX_UTILS_NO_USE_LIMIT 1
10 #define EX_UTILS_NO_USE_INPUT 1
11 #define EX_UTILS_NO_USE_BLOCK 1
12 #define EX_UTILS_NO_USE_PUT 1
13 #define EX_UTILS_NO_USE_BLOCKING_OPEN 1
14 #define EX_UTILS_USE_NONBLOCKING_OPEN 1
15 #define EX_UTILS_RET_FAIL 1
16 #include "ex_utils.h"
17
18 #define HTTPD_CONF_REQ__X_CONTENT_VSTR(x) do { \
19 if (!req->xtra_content && !(req->xtra_content = vstr_make_base(NULL))) \
20 return (FALSE); \
21 \
22 if (conf_sc_token_app_vstr(conf, token, req->xtra_content, \
23 &req-> x ## _vs1, \
24 &req-> x ## _pos, \
25 &req-> x ## _len)) \
26 return (FALSE); \
27 \
28 if ((token->type == CONF_TOKEN_TYPE_QUOTE_ESC_D) || \
29 (token->type == CONF_TOKEN_TYPE_QUOTE_ESC_DDD) || \
30 (token->type == CONF_TOKEN_TYPE_QUOTE_ESC_S) || \
31 (token->type == CONF_TOKEN_TYPE_QUOTE_ESC_SSS) || \
32 FALSE) \
33 if (!req-> x ## _vs1 || \
34 !conf_sc_conv_unesc(req->xtra_content, \
35 req-> x ## _pos, \
36 req-> x ## _len, &req-> x ## _len)) \
37 return (FALSE); \
38 } while (FALSE)
39
40
41 #define HTTPD_CONF_REQ__X_HDR_CHK(x, y, z) do { \
42 if (!req->policy->allow_hdr_split && \
43 vstr_srch_cstr_chrs_fwd(x, y, z, HTTP_EOL)) \
44 return (FALSE); \
45 if (!req->policy->allow_hdr_nil && \
46 vstr_srch_chr_fwd(x, y, z, 0)) \
47 return (FALSE); \
48 } while (FALSE)
49
50 #define HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(x) \
51 HTTPD_CONF_REQ__X_HDR_CHK(req-> x ## _vs1, req-> x ## _pos, req-> x ## _len)
52
53 #define HTTP__CONTENT_PARAMS(req, x) \
54 (req)-> x ## _vs1, (req)-> x ## _pos, (req)-> x ## _len
55
56
57 #define HTTPD_CONF_REQ__TYPE_BUILD_PATH_SKIP 0
58 #define HTTPD_CONF_REQ__TYPE_BUILD_PATH_ASSIGN 1
59 #define HTTPD_CONF_REQ__TYPE_BUILD_PATH_APPEND 2
60
61
62
63
64
httpd__conf_req_reset_expires(struct Httpd_req_data * req,time_t now)65 static void httpd__conf_req_reset_expires(struct Httpd_req_data *req,
66 time_t now)
67 {
68 Vstr_base *s1 = req->xtra_content;
69 size_t pos = req->expires_pos;
70 size_t len = req->expires_len;
71 Opt_serv_opts *opts = req->policy->s->beg;
72 Httpd_opts *hopts = (Httpd_opts *)opts;
73
74 ASSERT(vstr_sc_poslast(pos, len) == s1->len);
75
76 if (vstr_sub_cstr_buf(s1, pos, len, date_rfc1123(hopts->date, now)))
77 req->expires_len = vstr_sc_posdiff(pos, s1->len);
78 }
79
httpd__conf_req_reset_cache_control(struct Httpd_req_data * req,unsigned int val)80 static void httpd__conf_req_reset_cache_control(struct Httpd_req_data *req,
81 unsigned int val)
82 {
83 Vstr_base *s1 = req->xtra_content;
84 size_t pos = req->cache_control_pos;
85 size_t len = req->cache_control_len;
86
87 ASSERT(pos && len);
88 ASSERT(vstr_sc_poslast(pos, len) == s1->len);
89
90 if (vstr_add_fmt(s1, pos - 1, "max-age=%u", val))
91 {
92 vstr_sc_reduce(s1, 1, s1->len, len);
93 req->cache_control_len = vstr_sc_posdiff(pos, s1->len);
94 }
95 }
96
httpd__build_path(struct Con * con,Httpd_req_data * req,const Conf_parse * conf,Conf_token * token,Vstr_base * s1,size_t pos,size_t len,unsigned int lim,int full,Vstr_ref * ref,int uri_fname,int * type)97 static int httpd__build_path(struct Con *con, Httpd_req_data *req,
98 const Conf_parse *conf, Conf_token *token,
99 Vstr_base *s1, size_t pos, size_t len,
100 unsigned int lim, int full, Vstr_ref *ref,
101 int uri_fname, int *type)
102 {
103 int dummy_type;
104 int clist = FALSE;
105
106 if (!type) type = &dummy_type;
107 *type = HTTPD_CONF_REQ__TYPE_BUILD_PATH_SKIP;
108
109 if (uri_fname)
110 {
111 int slash_dot_safe = FALSE;
112
113 if (req->chk_encoded_slash && req->chk_encoded_dot)
114 slash_dot_safe = TRUE;
115 if (!httpd_policy_uri_lim_eq(s1, &pos, &len, lim, slash_dot_safe, ref))
116 {
117 conf_parse_end_token(conf, token, conf_token_at_depth(token));
118 return (TRUE);
119 }
120 }
121 else
122 {
123 size_t vhost_len = req->vhost_prefix_len;
124
125 if (!httpd_policy_path_lim_eq(s1, &pos, &len, lim, vhost_len, ref))
126 {
127 conf_parse_end_token(conf, token, conf_token_at_depth(token));
128 return (TRUE);
129 }
130
131 if (full && vhost_len)
132 { /* don't want to keep the vhost data */
133 if (lim != HTTPD_POLICY_PATH_LIM_NONE)
134 pos -= vhost_len;
135 else
136 len -= vhost_len;
137
138 ASSERT(req->fname == s1);
139 vstr_del(req->fname, 1, vhost_len);
140 req->vhost_prefix_len = 0;
141 }
142 }
143 *type = HTTPD_CONF_REQ__TYPE_BUILD_PATH_ASSIGN;
144
145 CONF_SC_TOGGLE_CLIST_VAR(clist);
146
147 if (0) { }
148
149 else if (OPT_SERV_SYM_EQ("assign") || OPT_SERV_SYM_EQ("="))
150 {
151 if (!httpd_policy_build_path(con, req, conf, token, NULL, NULL))
152 return (FALSE);
153
154 if (!vstr_sub_vstr(s1, pos, len,
155 conf->tmp, 1, conf->tmp->len, VSTR_TYPE_SUB_BUF_REF))
156 return (FALSE);
157 }
158 else if (OPT_SERV_SYM_EQ("append") || OPT_SERV_SYM_EQ("+="))
159 {
160 *type = HTTPD_CONF_REQ__TYPE_BUILD_PATH_APPEND;
161
162 if (!httpd_policy_build_path(con, req, conf, token, NULL, NULL))
163 return (FALSE);
164
165 return (vstr_add_vstr(s1, vstr_sc_poslast(pos, len),
166 conf->tmp, 1, conf->tmp->len, VSTR_TYPE_ADD_BUF_REF));
167 }
168 else if (!clist)
169 {
170 const Vstr_sect_node *pv = conf_token_value(token);
171
172 if (!pv || !vstr_sub_vstr(s1, pos, len, conf->data, pv->pos, pv->len,
173 VSTR_TYPE_SUB_BUF_REF))
174 return (FALSE);
175 OPT_SERV_X__ESC_VSTR(s1, pos, pv->len);
176
177 goto fin_overwrite;
178 }
179 else
180 return (FALSE);
181
182 fin_overwrite:
183 if (lim == HTTPD_POLICY_PATH_LIM_NONE)
184 req->vhost_prefix_len = 0; /* replaced everything */
185
186 return (TRUE);
187 }
188
httpd__meta_build_path(struct Con * con,Httpd_req_data * req,Conf_parse * conf,Conf_token * token,int * full,unsigned int * lim,Vstr_ref ** ret_ref)189 static int httpd__meta_build_path(struct Con *con, Httpd_req_data *req,
190 Conf_parse *conf, Conf_token *token,
191 int *full,
192 unsigned int *lim, Vstr_ref **ret_ref)
193 {
194 unsigned int depth = token->depth_num;
195
196 ASSERT(ret_ref && !*ret_ref);
197
198 if (token->type != CONF_TOKEN_TYPE_SLIST)
199 return (TRUE);
200
201 while (conf_token_list_num(token, depth))
202 {
203 int clist = FALSE;
204
205 CONF_SC_PARSE_DEPTH_TOKEN_RET(conf, token, depth, TRUE);
206 CONF_SC_TOGGLE_CLIST_VAR(clist);
207
208 if (0) { }
209 else if (full && (OPT_SERV_SYM_EQ("skip-virtual-hosts") ||
210 OPT_SERV_SYM_EQ("skip-vhosts")))
211 OPT_SERV_X_TOGGLE(*full);
212 else if (full && OPT_SERV_SYM_EQ("skip-document-root"))
213 OPT_SERV_X_TOGGLE(req->skip_document_root);
214 else if (OPT_SERV_SYM_EQ("limit"))
215 {
216 CONF_SC_PARSE_TOP_TOKEN_RET(conf, token, FALSE);
217
218 vstr_ref_del(*ret_ref); *ret_ref = NULL;
219 if (token->type >= CONF_TOKEN_TYPE_USER_BEG)
220 {
221 unsigned int type = token->type - CONF_TOKEN_TYPE_USER_BEG;
222 unsigned int nxt = 0;
223 Vstr_ref *ref = conf_token_get_user_value(conf, token, &nxt);
224
225 switch (type)
226 {
227 case HTTPD_POLICY_REQ_PATH_BEG:
228 case HTTPD_POLICY_REQ_PATH_END:
229 case HTTPD_POLICY_REQ_PATH_EQ:
230
231 case HTTPD_POLICY_REQ_NAME_BEG:
232 case HTTPD_POLICY_REQ_NAME_END:
233 case HTTPD_POLICY_REQ_NAME_EQ:
234
235 case HTTPD_POLICY_REQ_BWEN_BEG:
236 case HTTPD_POLICY_REQ_BWEN_END:
237 case HTTPD_POLICY_REQ_BWEN_EQ:
238
239 case HTTPD_POLICY_REQ_BWES_BEG:
240 case HTTPD_POLICY_REQ_BWES_END:
241 case HTTPD_POLICY_REQ_BWES_EQ:
242
243 case HTTPD_POLICY_REQ_EXTN_BEG:
244 case HTTPD_POLICY_REQ_EXTN_END:
245 case HTTPD_POLICY_REQ_EXTN_EQ:
246
247 case HTTPD_POLICY_REQ_EXTS_BEG:
248 case HTTPD_POLICY_REQ_EXTS_END:
249 case HTTPD_POLICY_REQ_EXTS_EQ:
250 *lim = httpd_policy_path_req2lim(type);
251 break;
252
253 default:
254 vstr_ref_del(ref);
255 return (FALSE);
256 }
257 *ret_ref = ref;
258 if (nxt && !conf_parse_num_token(conf, token, nxt))
259 return (FALSE);
260 }
261
262 else if (OPT_SERV_SYM_EQ("<none>"))
263 *lim = HTTPD_POLICY_PATH_LIM_NONE;
264 else if (OPT_SERV_SYM_EQ("<path>"))
265 *lim = HTTPD_POLICY_PATH_LIM_PATH_FULL;
266 else if (OPT_SERV_SYM_EQ("<basename>"))
267 *lim = HTTPD_POLICY_PATH_LIM_NAME_FULL;
268 else if (OPT_SERV_SYM_EQ("<extension>"))
269 *lim = HTTPD_POLICY_PATH_LIM_EXTN_FULL;
270 else if (OPT_SERV_SYM_EQ("<extensions>"))
271 *lim = HTTPD_POLICY_PATH_LIM_EXTS_FULL;
272 else if (OPT_SERV_SYM_EQ("<basename-without-extension>"))
273 *lim = HTTPD_POLICY_PATH_LIM_BWEN_FULL;
274 else if (OPT_SERV_SYM_EQ("<basename-without-extensions>"))
275 *lim = HTTPD_POLICY_PATH_LIM_BWES_FULL;
276 else
277 {
278 unsigned int type = HTTPD_POLICY_REQ_PATH_BEG;
279
280 if (0) { }
281 else if (OPT_SERV_SYM_EQ("<path>-beg"))
282 type = HTTPD_POLICY_REQ_PATH_BEG;
283 else if (OPT_SERV_SYM_EQ("<path>-end"))
284 type = HTTPD_POLICY_REQ_PATH_END;
285 else if (OPT_SERV_SYM_EQ("<path>-eq") || OPT_SERV_SYM_EQ("<path>=="))
286 type = HTTPD_POLICY_REQ_PATH_EQ;
287 else if (OPT_SERV_SYM_EQ("<basename>-beg") ||
288 OPT_SERV_SYM_EQ("<basename>=="))
289 type = HTTPD_POLICY_REQ_NAME_BEG;
290 else if (OPT_SERV_SYM_EQ("<basename>-end"))
291 type = HTTPD_POLICY_REQ_NAME_END;
292 else if (OPT_SERV_SYM_EQ("<basename>-eq") ||
293 OPT_SERV_SYM_EQ("<basename>=="))
294 type = HTTPD_POLICY_REQ_NAME_EQ;
295 else if (OPT_SERV_SYM_EQ("<extension>-beg"))
296 type = HTTPD_POLICY_REQ_EXTN_BEG;
297 else if (OPT_SERV_SYM_EQ("<extension>-end"))
298 type = HTTPD_POLICY_REQ_EXTN_END;
299 else if (OPT_SERV_SYM_EQ("<extension>-eq") ||
300 OPT_SERV_SYM_EQ("<extension>=="))
301 type = HTTPD_POLICY_REQ_EXTN_EQ;
302 else if (OPT_SERV_SYM_EQ("<extensions>-beg"))
303 type = HTTPD_POLICY_REQ_EXTS_BEG;
304 else if (OPT_SERV_SYM_EQ("<extensions>-end"))
305 type = HTTPD_POLICY_REQ_EXTS_END;
306 else if (OPT_SERV_SYM_EQ("<extensions>-eq") ||
307 OPT_SERV_SYM_EQ("<extensions>=="))
308 type = HTTPD_POLICY_REQ_EXTS_EQ;
309 else if (OPT_SERV_SYM_EQ("<basename-without-extension>-beg"))
310 type = HTTPD_POLICY_REQ_BWEN_BEG;
311 else if (OPT_SERV_SYM_EQ("<basename-without-extension>-end"))
312 type = HTTPD_POLICY_REQ_BWEN_END;
313 else if (OPT_SERV_SYM_EQ("<basename-without-extension>-eq") ||
314 OPT_SERV_SYM_EQ("<basename-without-extension>=="))
315 type = HTTPD_POLICY_REQ_BWEN_EQ;
316 else if (OPT_SERV_SYM_EQ("<basename-without-extensions>-beg"))
317 type = HTTPD_POLICY_REQ_BWES_BEG;
318 else if (OPT_SERV_SYM_EQ("<basename-without-extensions>-end"))
319 type = HTTPD_POLICY_REQ_BWES_END;
320 else if (OPT_SERV_SYM_EQ("<basename-without-extensions>-eq") ||
321 OPT_SERV_SYM_EQ("<basename-without-extensions>=="))
322 type = HTTPD_POLICY_REQ_BWES_EQ;
323 else
324 return (FALSE);
325
326 if (!httpd_policy_path_make(con, req, conf, token, type, ret_ref))
327 return (FALSE);
328
329 *lim = httpd_policy_path_req2lim(type);
330 }
331 }
332 else
333 return (FALSE);
334 }
335
336 CONF_SC_PARSE_DEPTH_TOKEN_RET(conf, token, depth - 1, FALSE);
337 return (TRUE);
338 }
339
httpd__content_location_valid(Httpd_req_data * req,size_t * ret_pos,size_t * ret_len)340 static int httpd__content_location_valid(Httpd_req_data *req,
341 size_t *ret_pos, size_t *ret_len)
342 {
343 size_t pos = 0;
344 size_t len = 0;
345
346 ASSERT(req->content_location_vs1);
347
348 pos = req->content_location_pos;
349 len = req->content_location_len;
350 if (vstr_sc_poslast(pos, len) != req->xtra_content->len)
351 {
352 size_t tmp = req->xtra_content->len + 1;
353 if (!HTTPD_APP_REF_VSTR(req->xtra_content, req->xtra_content, pos, len))
354 return (FALSE);
355 pos = tmp;
356 ASSERT(len == vstr_sc_posdiff(pos, req->xtra_content->len));
357 }
358 *ret_pos = pos;
359 *ret_len = len;
360
361 return (TRUE);
362 }
363
364 /* we negotiate a few items, and this is the loop for all of them... */
365 #define HTTPD__NEG_BEG(x) \
366 unsigned int depth = token->depth_num; \
367 unsigned int max_qual = 0; \
368 unsigned int qual_num = 0; \
369 unsigned int neg_count = 0; \
370 Conf_token save; \
371 \
372 save = *token; \
373 while (conf_token_list_num(token, depth) && \
374 (!(x) || (++neg_count <= (x)))) \
375 { \
376 unsigned int qual = 0; \
377 const Vstr_sect_node *val = NULL; \
378 \
379 CONF_SC_PARSE_DEPTH_TOKEN_RET(conf, token, depth, TRUE); \
380 CONF_SC_TOGGLE_CLIST_VAR(clist); \
381 \
382 if (!((token->type == CONF_TOKEN_TYPE_QUOTE_D) || \
383 (token->type == CONF_TOKEN_TYPE_QUOTE_DDD) || \
384 (token->type == CONF_TOKEN_TYPE_QUOTE_S) || \
385 (token->type == CONF_TOKEN_TYPE_QUOTE_SSS) || \
386 (token->type == CONF_TOKEN_TYPE_SYMBOL))) \
387 return (FALSE); \
388 \
389 val = conf_token_value(token)
390
391
392 #define HTTPD__NEG_END() \
393 if (qual > max_qual) \
394 { \
395 max_qual = qual; \
396 qual_num = token->num - 1; \
397 } \
398 \
399 CONF_SC_PARSE_TOP_TOKEN_RET(conf, token, FALSE); \
400 } \
401 if (neg_count) \
402 conf_parse_end_token(conf, token, depth); \
403 do { } while (FALSE)
404
405
httpd__conf_req_d1(struct Con * con,struct Httpd_req_data * req,time_t file_timestamp,Conf_parse * conf,Conf_token * token,int clist)406 static int httpd__conf_req_d1(struct Con *con, struct Httpd_req_data *req,
407 time_t file_timestamp,
408 Conf_parse *conf, Conf_token *token, int clist)
409 {
410 if (0) { }
411
412 else if (OPT_SERV_SYM_EQ("match-init"))
413 OPT_SERV_SC_MATCH_INIT(req->policy->s->beg,
414 httpd__conf_req_d1(con, req, file_timestamp,
415 conf, token, clist));
416
417 else if (OPT_SERV_SYM_EQ("return"))
418 {
419 unsigned int code = 0;
420
421 if (conf_sc_token_parse_uint(conf, token, &code))
422 return (FALSE);
423
424 req->user_return_error_code = TRUE;
425 ASSERT(!req->error_code);
426 req->error_code = 0;
427 switch (code)
428 {
429 case 301: if (!req->error_code && req->direct_uri) HTTPD_ERR_301(req);
430 case 302: if (!req->error_code && req->direct_uri) HTTPD_ERR_302(req);
431 case 303: if (!req->error_code && req->direct_uri) HTTPD_ERR_303(req);
432 case 307: if (!req->error_code && req->direct_uri) HTTPD_ERR_307(req);
433 default:
434 if (req->error_code)
435 httpd_req_absolute_uri(con, req, req->fname, 1, req->fname->len);
436 else
437 req->user_return_error_code = FALSE;
438 return (FALSE);
439
440 case 400: HTTPD_ERR_RET(req, 400, FALSE);
441 case 403: HTTPD_ERR_RET(req, 403, FALSE);
442 case 404: HTTPD_ERR_RET(req, 404, FALSE);
443 case 410: HTTPD_ERR_RET(req, 410, FALSE);
444 case 500: HTTPD_ERR_RET(req, 500, FALSE);
445 case 503: HTTPD_ERR_RET(req, 503, FALSE);
446 }
447 }
448 else if (OPT_SERV_SYM_EQ("Location:"))
449 {
450 unsigned int lim = HTTPD_POLICY_PATH_LIM_NONE;
451 Vstr_ref *ref = NULL;
452 size_t orig_len = 0;
453 int bp_type = HTTPD_CONF_REQ__TYPE_BUILD_PATH_SKIP;
454
455 if (req->direct_filename)
456 return (FALSE);
457
458 CONF_SC_PARSE_TOP_TOKEN_RET(conf, token, FALSE);
459 if (!httpd__meta_build_path(con, req, conf, token, NULL, &lim, &ref))
460 {
461 vstr_ref_del(ref);
462 return (FALSE);
463 }
464 if (!req->direct_uri)
465 {
466 if (lim == HTTPD_POLICY_PATH_LIM_NONE)
467 orig_len = req->fname->len; /* don't do more than we need */
468 else if (!vstr_sub_vstr(req->fname, 1, req->fname->len,
469 con->evnt->io_r, req->path_pos, req->path_len, 0))
470 {
471 vstr_ref_del(ref);
472 return (FALSE);
473 }
474 }
475 if (!httpd__build_path(con, req, conf, token,
476 req->fname, 1, req->fname->len,
477 lim, FALSE, ref, TRUE, &bp_type))
478 return (FALSE);
479 if (!req->direct_uri && (lim == HTTPD_POLICY_PATH_LIM_NONE) &&
480 (bp_type != HTTPD_CONF_REQ__TYPE_BUILD_PATH_ASSIGN))
481 { /* we needed to do the above sub */
482 if (!vstr_sub_vstr(req->fname, 1, orig_len,
483 con->evnt->io_r, req->path_pos, req->path_len, 0))
484 return (FALSE);
485 }
486
487 req->direct_uri = TRUE;
488 HTTPD_CONF_REQ__X_HDR_CHK(req->fname, 1, req->fname->len);
489 }
490 else if (OPT_SERV_SYM_EQ("Cache-Control:"))
491 {
492 HTTPD_CONF_REQ__X_CONTENT_VSTR(cache_control);
493 if (vstr_cmp_cstr_eq(HTTP__CONTENT_PARAMS(req, cache_control),
494 "<expires-now>"))
495 httpd__conf_req_reset_cache_control(req, 0);
496 else if (vstr_cmp_cstr_eq(HTTP__CONTENT_PARAMS(req, cache_control),
497 "<expire-minute>"))
498 httpd__conf_req_reset_cache_control(req, (60 * 1));
499 else if (vstr_cmp_cstr_eq(HTTP__CONTENT_PARAMS(req, cache_control),
500 "<expire-hour>"))
501 httpd__conf_req_reset_cache_control(req, (60 * 60));
502 else if (vstr_cmp_cstr_eq(HTTP__CONTENT_PARAMS(req, cache_control),
503 "<expire-day>"))
504 httpd__conf_req_reset_cache_control(req, (60 * 60 * 24));
505 else if (vstr_cmp_cstr_eq(HTTP__CONTENT_PARAMS(req, cache_control),
506 "<expire-week>"))
507 httpd__conf_req_reset_cache_control(req, (60 * 60 * 24 * 7));
508 else if (vstr_cmp_cstr_eq(HTTP__CONTENT_PARAMS(req, cache_control),
509 "<expires-never>"))
510 httpd__conf_req_reset_cache_control(req, (60 * 60 * 24 * 365));
511 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(cache_control);
512 }
513 else if (OPT_SERV_SYM_EQ("Content-Disposition:"))
514 {
515 HTTPD_CONF_REQ__X_CONTENT_VSTR(content_disposition);
516 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(content_disposition);
517 }
518 else if (OPT_SERV_SYM_EQ("Content-Language:"))
519 {
520 HTTPD_CONF_REQ__X_CONTENT_VSTR(content_language);
521 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(content_language);
522 }
523 else if (OPT_SERV_SYM_EQ("Content-Location:"))
524 {
525 unsigned int lim = HTTPD_POLICY_PATH_LIM_NONE;
526 size_t pos = 0;
527 size_t len = 0;
528 Vstr_ref *ref = NULL;
529
530 if (req->direct_uri || req->direct_filename)
531 return (FALSE);
532
533 if (!req->xtra_content && !(req->xtra_content = vstr_make_base(NULL)))
534 return (FALSE);
535
536 if (req->content_location_vs1)
537 {
538 if (!httpd__content_location_valid(req, &pos, &len))
539 return (FALSE);
540 }
541 else
542 {
543 pos = req->xtra_content->len + 1;
544 if (!HTTPD_APP_REF_VSTR(req->xtra_content,
545 con->evnt->io_r, req->path_pos, req->path_len))
546 return (FALSE);
547 ASSERT(pos <= req->xtra_content->len);
548 len = vstr_sc_posdiff(pos, req->xtra_content->len);
549 }
550
551 CONF_SC_PARSE_TOP_TOKEN_RET(conf, token, FALSE);
552 if (!httpd__meta_build_path(con, req, conf, token, NULL, &lim, &ref))
553 {
554 vstr_ref_del(ref);
555 return (FALSE);
556 }
557 if (!httpd__build_path(con, req, conf, token, req->xtra_content, pos, len,
558 lim, FALSE, ref, TRUE, NULL))
559 return (FALSE);
560
561 len = vstr_sc_posdiff(pos, req->xtra_content->len);
562
563 req->content_location_vs1 = req->xtra_content;
564 req->content_location_pos = pos;
565 req->content_location_len = len;
566 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(content_location);
567 }
568 else if (OPT_SERV_SYM_EQ("Content-MD5:") ||
569 OPT_SERV_SYM_EQ("identity/Content-MD5:"))
570 {
571 req->content_md5_time = file_timestamp;
572 HTTPD_CONF_REQ__X_CONTENT_VSTR(content_md5);
573 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(content_md5);
574 }
575 else if (OPT_SERV_SYM_EQ("gzip/Content-MD5:"))
576 {
577 req->content_md5_time = file_timestamp;
578 HTTPD_CONF_REQ__X_CONTENT_VSTR(gzip_content_md5);
579 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(gzip_content_md5);
580 }
581 else if (OPT_SERV_SYM_EQ("bzip2/Content-MD5:"))
582 {
583 req->content_md5_time = file_timestamp;
584 HTTPD_CONF_REQ__X_CONTENT_VSTR(bzip2_content_md5);
585 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(bzip2_content_md5);
586 }
587 else if (OPT_SERV_SYM_EQ("Content-Type:"))
588 {
589 HTTPD_CONF_REQ__X_CONTENT_VSTR(content_type);
590 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(content_type);
591 }
592 else if (OPT_SERV_SYM_EQ("ETag:") ||
593 OPT_SERV_SYM_EQ("identity/ETag:"))
594 {
595 req->etag_time = file_timestamp;
596 HTTPD_CONF_REQ__X_CONTENT_VSTR(etag);
597 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(etag);
598 }
599 else if (OPT_SERV_SYM_EQ("gzip/ETag:"))
600 {
601 req->etag_time = file_timestamp;
602 HTTPD_CONF_REQ__X_CONTENT_VSTR(gzip_etag);
603 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(gzip_etag);
604 }
605 else if (OPT_SERV_SYM_EQ("bzip2/ETag:"))
606 {
607 req->etag_time = file_timestamp;
608 HTTPD_CONF_REQ__X_CONTENT_VSTR(bzip2_etag);
609 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(bzip2_etag);
610 }
611 else if (OPT_SERV_SYM_EQ("Expires:"))
612 {
613 HTTPD_CONF_REQ__X_CONTENT_VSTR(expires);
614 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(expires);
615
616 /* note that rfc2616 says only go upto a year into the future */
617 req->expires_time = req->now;
618 if (vstr_cmp_cstr_eq(HTTP__CONTENT_PARAMS(req, expires), "<now>"))
619 httpd__conf_req_reset_expires(req, req->now);
620 else if (vstr_cmp_cstr_eq(HTTP__CONTENT_PARAMS(req, expires), "<minute>"))
621 httpd__conf_req_reset_expires(req, req->now + (60 * 1));
622 else if (vstr_cmp_cstr_eq(HTTP__CONTENT_PARAMS(req, expires), "<hour>"))
623 httpd__conf_req_reset_expires(req, req->now + (60 * 60));
624 else if (vstr_cmp_cstr_eq(HTTP__CONTENT_PARAMS(req, expires), "<day>"))
625 httpd__conf_req_reset_expires(req, req->now + (60 * 60 * 24));
626 else if (vstr_cmp_cstr_eq(HTTP__CONTENT_PARAMS(req, expires), "<week>"))
627 httpd__conf_req_reset_expires(req, req->now + (60 * 60 * 24 * 7));
628 else if (vstr_cmp_cstr_eq(HTTP__CONTENT_PARAMS(req, expires), "<never>"))
629 httpd__conf_req_reset_expires(req, req->now + (60 * 60 * 24 * 365));
630 else
631 req->expires_time = file_timestamp;
632 }
633 else if (OPT_SERV_SYM_EQ("Link:")) /* rfc2068 -- old, but still honored */
634 {
635 HTTPD_CONF_REQ__X_CONTENT_VSTR(link);
636 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(link);
637 }
638 else if (OPT_SERV_SYM_EQ("P3P:")) /* http://www.w3.org/TR/P3P/ */
639 {
640 HTTPD_CONF_REQ__X_CONTENT_VSTR(p3p);
641 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(p3p);
642 }
643 /* else if (OPT_SERV_SYM_EQ("Etag:"))
644 X_CONTENT_VSTR(etag); -- needs server support */
645 else if (OPT_SERV_SYM_EQ("filename"))
646 {
647 int full = req->skip_document_root;
648 unsigned int lim = HTTPD_POLICY_PATH_LIM_NAME_FULL;
649 Vstr_ref *ref = NULL;
650
651 if (req->direct_uri)
652 return (FALSE);
653 CONF_SC_PARSE_TOP_TOKEN_RET(conf, token, FALSE);
654 if (!httpd__meta_build_path(con, req, conf, token, &full, &lim, &ref))
655 {
656 vstr_ref_del(ref);
657 return (FALSE);
658 }
659 if (!httpd__build_path(con, req, conf, token,
660 req->fname, 1, req->fname->len,
661 lim, full, ref, FALSE, NULL))
662 return (FALSE);
663 req->direct_filename = TRUE;
664 }
665 else if (OPT_SERV_SYM_EQ("parse-accept"))
666 OPT_SERV_X_TOGGLE(req->parse_accept);
667 else if (OPT_SERV_SYM_EQ("parse-accept-language"))
668 OPT_SERV_X_TOGGLE(req->parse_accept_language);
669 else if (OPT_SERV_SYM_EQ("allow-accept-encoding"))
670 OPT_SERV_X_TOGGLE(req->allow_accept_encoding);
671 else if (OPT_SERV_SYM_EQ("Vary:_*"))
672 OPT_SERV_X_TOGGLE(req->vary_star);
673 else if (OPT_SERV_SYM_EQ("Vary:_Accept"))
674 OPT_SERV_X_TOGGLE(req->vary_a);
675 else if (OPT_SERV_SYM_EQ("Vary:_Accept-Charset"))
676 OPT_SERV_X_TOGGLE(req->vary_ac);
677 else if (OPT_SERV_SYM_EQ("Vary:_Accept-Language"))
678 OPT_SERV_X_TOGGLE(req->vary_al);
679 else if (OPT_SERV_SYM_EQ("Vary:_Referer") ||
680 OPT_SERV_SYM_EQ("Vary:_Referrer"))
681 OPT_SERV_X_TOGGLE(req->vary_rf);
682 else if (OPT_SERV_SYM_EQ("Vary:_User-Agent"))
683 OPT_SERV_X_TOGGLE(req->vary_ua);
684 else if (OPT_SERV_SYM_EQ("negotiate-content-type"))
685 {
686 if (req->neg_content_type_done)
687 return (FALSE);
688 HTTPD__NEG_BEG(req->policy->max_neg_A_nodes);
689 qual = http_parse_accept(req, conf->data, val->pos, val->len);
690 HTTPD__NEG_END();
691
692 req->neg_content_type_done = TRUE;
693 req->vary_a = TRUE;
694 if (qual_num)
695 {
696 unsigned int last = token->num;
697
698 req->parse_accept = FALSE;
699 *token = save;
700 conf_parse_num_token(conf, token, qual_num);
701 HTTPD_CONF_REQ__X_CONTENT_VSTR(content_type);
702 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(content_type);
703 HTTPD_CONF_REQ__X_CONTENT_VSTR(ext_vary_a);
704 conf_parse_num_token(conf, token, last);
705 }
706 }
707 else if (OPT_SERV_SYM_EQ("negotiate-charset"))
708 return (FALSE);
709 else if (OPT_SERV_SYM_EQ("negotiate-content-language"))
710 {
711 if (req->neg_content_lang_done)
712 return (FALSE);
713 HTTPD__NEG_BEG(req->policy->max_neg_AL_nodes);
714 qual = http_parse_accept_language(req, conf->data, val->pos, val->len);
715 HTTPD__NEG_END();
716
717 req->neg_content_lang_done = TRUE;
718 req->vary_al = TRUE;
719 if (qual_num)
720 {
721 unsigned int last = token->num;
722
723 req->parse_accept_language = FALSE;
724 *token = save;
725 conf_parse_num_token(conf, token, qual_num);
726 HTTPD_CONF_REQ__X_CONTENT_VSTR(content_language);
727 HTTPD_CONF_REQ__X_CONTENT_HDR_CHK(content_language);
728 HTTPD_CONF_REQ__X_CONTENT_VSTR(ext_vary_al);
729 conf_parse_num_token(conf, token, last);
730 }
731 }
732 else
733 return (FALSE);
734
735 return (TRUE);
736 }
737 #undef HTTPD__NEG_BEG
738 #undef HTTPD__NEG_END
739
httpd_conf_req_d0(struct Con * con,Httpd_req_data * req,time_t timestamp,Conf_parse * conf,Conf_token * token)740 int httpd_conf_req_d0(struct Con *con, Httpd_req_data *req,
741 time_t timestamp,
742 Conf_parse *conf, Conf_token *token)
743 {
744 unsigned int cur_depth = token->depth_num;
745
746 if (!OPT_SERV_SYM_EQ("org.and.jhttpd-conf-req-1.0"))
747 return (FALSE);
748
749 while (conf_token_list_num(token, cur_depth))
750 {
751 int clist = FALSE;
752
753 CONF_SC_PARSE_DEPTH_TOKEN_RET(conf, token, cur_depth, FALSE);
754 CONF_SC_TOGGLE_CLIST_VAR(clist);
755
756 if (!httpd__conf_req_d1(con, req, timestamp, conf, token, clist))
757 return (FALSE);
758 }
759
760 if (req->content_location_vs1)
761 { /* absolute URI content-location header */
762 size_t pos = 0;
763 size_t len = 0;
764
765 if (!httpd__content_location_valid(req, &pos, &len))
766 return (FALSE);
767
768 httpd_req_absolute_uri(con, req, req->xtra_content, pos, len);
769 req->content_location_len = vstr_sc_posdiff(pos, req->xtra_content->len);
770 }
771
772 return (TRUE);
773 }
774
httpd_conf_req_parse_file(Conf_parse * conf,struct Con * con,Httpd_req_data * req)775 int httpd_conf_req_parse_file(Conf_parse *conf,
776 struct Con *con, Httpd_req_data *req)
777 {
778 Conf_token token[1] = {CONF_TOKEN_INIT};
779 Vstr_base *dir = req->policy->req_conf_dir;
780 Vstr_base *fname = req->fname;
781 Vstr_base *s1 = NULL;
782 const char *fname_cstr = NULL;
783 int fd = -1;
784 struct stat64 cf_stat[1];
785
786 ASSERT(conf && con && req && dir && fname && fname->len);
787
788 if (!dir->len ||
789 !req->policy->use_req_conf || req->skip_document_root)
790 return (TRUE);
791
792 s1 = conf->tmp;
793 HTTPD_APP_REF_ALLVSTR(s1, dir);
794 ASSERT((dir->len >= 1) && vstr_cmp_cstr_eq(dir, dir->len, 1, "/"));
795 ASSERT((fname->len >= 1) && vstr_cmp_cstr_eq(fname, 1, 1, "/"));
796 HTTPD_APP_REF_VSTR(s1, fname, 2, fname->len - 1);
797
798 if (s1->conf->malloc_bad ||
799 !(fname_cstr = vstr_export_cstr_ptr(s1, 1, s1->len)))
800 goto read_fail;
801
802 fd = io_open_nonblock(fname_cstr);
803 if ((fd == -1) && (errno == EISDIR))
804 goto fin_dir;
805
806 if ((fd == -1) &&
807 ((errno == ENOTDIR) || /* part of path was not a dir */
808 (errno == ENAMETOOLONG)))
809 goto fin_file; /* ignore these errors, not local users fault */
810
811 if ((fd == -1) && (errno == ENOENT))
812 goto fin_ok; /* ignore these errors, not local users fault */
813
814 if (fd == -1)
815 goto read_fail; /* this is "bad" */
816
817 if (fstat64(fd, cf_stat) == -1)
818 goto close_read_fail; /* this is "bad" */
819
820 if (S_ISDIR(cf_stat->st_mode))
821 goto fin_close_dir; /* ignore */
822
823 if (!S_ISREG(cf_stat->st_mode))
824 goto close_read_fail; /* this is "bad" */
825
826 if ((cf_stat->st_size < strlen("org.and.jhttpd-conf-req-1.0")) ||
827 (cf_stat->st_size > req->policy->max_req_conf_sz))
828 goto close_read_fail; /* this is "bad" */
829
830 s1 = conf->data;
831 vstr_del(conf->tmp, 1, conf->tmp->len); /* filename */
832
833 while (cf_stat->st_size > s1->len)
834 {
835 size_t len = cf_stat->st_size - s1->len;
836
837 if (!vstr_sc_read_len_fd(s1, s1->len, fd, len, NULL) ||
838 (len == (cf_stat->st_size - s1->len)))
839 goto close_read_fail;
840 }
841
842 close(fd);
843
844 if (!conf_parse_lex(conf, 1, conf->data->len))
845 goto conf_fail;
846
847 while (conf_parse_token(conf, token))
848 {
849 if ((token->type != CONF_TOKEN_TYPE_CLIST) || (token->depth_num != 1))
850 goto conf_fail;
851
852 if (!conf_parse_token(conf, token))
853 goto conf_fail;
854
855 if (!conf_token_cmp_sym_cstr_eq(conf, token, "org.and.jhttpd-conf-req-1.0"))
856 goto conf_fail;
857
858 if (!httpd_conf_req_d0(con, req, cf_stat->st_mtime, conf, token))
859 goto conf_fail;
860 }
861
862 /* And they all live together ... dum dum */
863 if (conf->data->conf->malloc_bad)
864 goto read_fail;
865
866 return (TRUE);
867
868 fin_close_dir:
869 close(fd);
870 fin_dir:
871 if (req->policy->use_secure_dirs)
872 { /* check if conf file exists inside the directory given,
873 * so we can re-direct without leaking info. */
874 struct stat64 d_stat[1];
875
876 vstr_add_cstr_buf(s1, s1->len, "/");
877 HTTPD_APP_REF_ALLVSTR(s1, req->policy->dir_filename);
878
879 fname_cstr = vstr_export_cstr_ptr(s1, 1, s1->len);
880 if (s1->conf->malloc_bad)
881 goto read_fail;
882 if ((stat64(fname_cstr, d_stat) != -1) && S_ISREG(d_stat->st_mode))
883 req->conf_secure_dirs = TRUE;
884 }
885
886 ASSERT(s1 == conf->tmp);
887 vstr_del(s1, 1, s1->len);
888 return (TRUE);
889
890 fin_file:
891 if (req->policy->use_friendly_dirs)
892 { /* check if conf file exists as a file, so we can re-direct backwards */
893 struct stat64 d_stat[1];
894 size_t len = vstr_cspn_cstr_chrs_rev(s1, 1, s1->len, "/") + 1;
895
896 if ((len > 1) && (len < (s1->len - req->vhost_prefix_len)))
897 { /* must be a filename, can't be toplevel */
898 vstr_sc_reduce(s1, 1, s1->len, len);
899
900 fname_cstr = vstr_export_cstr_ptr(s1, 1, s1->len);
901 if (s1->conf->malloc_bad)
902 goto read_fail;
903 if ((stat64(fname_cstr, d_stat) != -1) && S_ISREG(d_stat->st_mode))
904 req->conf_friendly_dirs = TRUE;
905 }
906 }
907
908 fin_ok:
909 ASSERT(s1 == conf->tmp);
910 vstr_del(s1, 1, s1->len);
911 return (TRUE);
912
913 close_read_fail:
914 vstr_del(conf->data, 1, conf->data->len);
915 vstr_del(conf->tmp, 1, conf->tmp->len);
916 close(fd);
917 goto read_fail;
918
919 conf_fail:
920 vstr_del(conf->tmp, 1, conf->tmp->len);
921 if (!req->user_return_error_code)
922 conf_parse_backtrace(conf->tmp, "<conf-request>", conf, token);
923 read_fail:
924 if (!req->user_return_error_code)
925 HTTPD_ERR(req, 503);
926 conf->data->conf->malloc_bad = FALSE;
927 return (FALSE);
928 }
929