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