]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: http: fix encoding of samples used in http headers
authorThierry FOURNIER <tfournier@exceliance.fr>
Thu, 13 Mar 2014 15:46:18 +0000 (16:46 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 17 Mar 2014 15:39:03 +0000 (16:39 +0100)
The binary samples are sometimes copied as is into http headers.
A sample can contain bytes unallowed by the http rfc concerning
header content, for example if it was extracted from binary data.
The resulting http request can thus be invalid.

This issue does not yet happen because haproxy currently (mistakenly)
hex-encodes binary data, so it is not really possible to retrieve
invalid HTTP chars.

The solution consists in hex-encoding all non-printable chars prefixed
by a '%' sign.

No backport is needed since existing code is not affected yet.

include/types/log.h
src/cfgparse.c
src/log.c
src/proto_http.c

index 66b418dec94e8544aaca543b2ef483eef0a943a8..8ee8d7c644b49f34357fe85d021f06eef599eed1 100644 (file)
@@ -125,6 +125,7 @@ struct logformat_node {
 #define LOG_OPT_QUOTE          0x00000004
 #define LOG_OPT_REQ_CAP         0x00000008
 #define LOG_OPT_RES_CAP         0x00000010
+#define LOG_OPT_HTTP            0x00000020
 
 
 /* Fields that need to be extracted from the incoming connection or request for
index 5589ec03f71f8559e73b8f3e02c9fea975ffca84..5cbb9e2a6bc6f0fb78d9a678d0d18703c285076f 100644 (file)
@@ -7085,7 +7085,7 @@ out_uri_auth_compat:
                        curproxy->conf.args.ctx = ARGC_UIF;
                        curproxy->conf.args.file = curproxy->conf.uif_file;
                        curproxy->conf.args.line = curproxy->conf.uif_line;
-                       parse_logformat_string(curproxy->conf.uniqueid_format_string, curproxy, &curproxy->format_unique_id, 0,
+                       parse_logformat_string(curproxy->conf.uniqueid_format_string, curproxy, &curproxy->format_unique_id, LOG_OPT_HTTP,
                                               (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR);
                        curproxy->conf.args.file = NULL;
                        curproxy->conf.args.line = 0;
index 3ab40f9c275087772f736353120113205927d7e1..f39168a08039323334b6ed3254cbcc0470a7b752 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -885,6 +885,7 @@ void __send_log(struct proxy *p, int level, char *message, size_t size)
 
 extern fd_set hdr_encode_map[];
 extern fd_set url_encode_map[];
+extern fd_set http_encode_map[];
 
 
 const char sess_cookie[8]     = "NIDVEOU7";    /* No cookie, Invalid cookie, cookie for a Down server, Valid cookie, Expired cookie, Old cookie, Unused, unknown */
@@ -940,6 +941,7 @@ int build_logline(struct session *s, char *dst, size_t maxsize, struct list *lis
                struct connection *conn;
                const char *src = NULL;
                struct sample *key;
+               const struct chunk empty = { NULL, 0, 0 };
 
                switch (tmp->type) {
                        case LOG_FMT_SEPARATOR:
@@ -964,7 +966,11 @@ int build_logline(struct session *s, char *dst, size_t maxsize, struct list *lis
                                        key = sample_fetch_string(be, s, txn, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, tmp->expr);
                                if (!key && (tmp->options & LOG_OPT_RES_CAP))
                                        key = sample_fetch_string(be, s, txn, SMP_OPT_DIR_RES|SMP_OPT_FINAL, tmp->expr);
-                               ret = lf_text_len(tmplog, key ? key->data.str.str : NULL, key ? key->data.str.len : 0, dst + maxsize - tmplog, tmp);
+                               if (tmp->options & LOG_OPT_HTTP)
+                                       ret = encode_chunk(tmplog, dst + maxsize,
+                                                          '%', http_encode_map, key ? &key->data.str : &empty);
+                               else
+                                       ret = lf_text_len(tmplog, key ? key->data.str.str : NULL, key ? key->data.str.len : 0, dst + maxsize - tmplog, tmp);
                                if (ret == 0)
                                        goto out;
                                tmplog = ret;
index c0be2898979def1214c36a92878d3f8b905cb7c7..0c6a6233f7b370ca687f7d9c7bffda98549d22b4 100644 (file)
@@ -235,6 +235,7 @@ static struct hdr_ctx static_hdr_ctx;
  */
 fd_set hdr_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
 fd_set url_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
+fd_set http_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
 
 #else
 #error "Check if your OS uses bitfields for fd_sets"
@@ -263,6 +264,7 @@ void init_proto_http()
         */
        memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
        memset(url_encode_map, 0, sizeof(url_encode_map));
+       memset(http_encode_map, 0, sizeof(url_encode_map));
        for (i = 0; i < 32; i++) {
                FD_SET(i, hdr_encode_map);
                FD_SET(i, url_encode_map);
@@ -284,6 +286,32 @@ void init_proto_http()
                tmp++;
        }
 
+       /* initialize the http header encoding map. The draft httpbis define the
+        * header content as:
+        *
+        *    HTTP-message   = start-line
+        *                     *( header-field CRLF )
+        *                     CRLF
+        *                     [ message-body ]
+        *    header-field   = field-name ":" OWS field-value OWS
+        *    field-value    = *( field-content / obs-fold )
+        *    field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
+        *    obs-fold       = CRLF 1*( SP / HTAB )
+        *    field-vchar    = VCHAR / obs-text
+        *    VCHAR          = %x21-7E
+        *    obs-text       = %x80-FF
+        *
+        * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
+        * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
+        * "obs-fold" is volontary forgotten because haproxy remove this.
+        */
+       memset(http_encode_map, 0, sizeof(http_encode_map));
+       for (i = 0x00; i <= 0x08; i++)
+               FD_SET(i, http_encode_map);
+       for (i = 0x0a; i <= 0x1f; i++)
+               FD_SET(i, http_encode_map);
+       FD_SET(0x7f, http_encode_map);
+
        /* memory allocations */
        pool2_requri = create_pool("requri", REQURI_LEN, MEM_F_SHARED);
        pool2_uniqueid = create_pool("uniqueid", UNIQUEID_LEN, MEM_F_SHARED);
@@ -8459,7 +8487,7 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i
                LIST_INIT(&rule->arg.hdr_add.fmt);
 
                proxy->conf.args.ctx = ARGC_HRQ;
-               parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, 0,
+               parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
                                       (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR);
                free(proxy->conf.lfs_file);
                proxy->conf.lfs_file = strdup(proxy->conf.args.file);
@@ -8630,7 +8658,7 @@ struct http_res_rule *parse_http_res_cond(const char **args, const char *file, i
                LIST_INIT(&rule->arg.hdr_add.fmt);
 
                proxy->conf.args.ctx = ARGC_HRS;
-               parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, 0,
+               parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
                                       (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR);
                free(proxy->conf.lfs_file);
                proxy->conf.lfs_file = strdup(proxy->conf.args.file);
@@ -8786,7 +8814,7 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st
                 */
                proxy->conf.args.ctx = ARGC_RDR;
                if (!(type == REDIRECT_TYPE_PREFIX && destination[0] == '/' && destination[1] == '\0')) {
-                       parse_logformat_string(destination, curproxy, &rule->rdr_fmt, 0,
+                       parse_logformat_string(destination, curproxy, &rule->rdr_fmt, LOG_OPT_HTTP,
                                               (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR);
                        free(curproxy->conf.lfs_file);
                        curproxy->conf.lfs_file = strdup(curproxy->conf.args.file);