From: Willy Tarreau Date: Fri, 20 Apr 2012 13:52:36 +0000 (+0200) Subject: MEDIUM: pattern: add an argument validation callback to pattern descriptors X-Git-Tag: v1.5-dev9~55 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=21d68a68953a1d03a7e881bc5586e908fbf38404;p=thirdparty%2Fhaproxy.git MEDIUM: pattern: add an argument validation callback to pattern descriptors This is used to validate that arguments are coherent. For instance, payload_lv expects that the last arg (if any) is not more negative than the sum of the first two. The error is reported if any. --- diff --git a/include/types/pattern.h b/include/types/pattern.h index c3718862d7..42dac41ac0 100644 --- a/include/types/pattern.h +++ b/include/types/pattern.h @@ -65,6 +65,8 @@ struct pattern_conv { int (*process)(const struct arg *arg_p, union pattern_data *data); /* process function */ unsigned int arg_mask; /* arguments (ARG*()) */ + int (*val_args)(struct arg *arg_p, + char **err_msg); /* argument validation function */ unsigned int in_type; /* input needed pattern type */ unsigned int out_type; /* output pattern type */ }; @@ -85,6 +87,8 @@ struct pattern_fetch { int dir, const struct arg *arg_p, union pattern_data *data); /* fetch processing function */ unsigned int arg_mask; /* arguments (ARG*()) */ + int (*val_args)(struct arg *arg_p, + char **err_msg); /* argument validation function */ unsigned long out_type; /* output pattern type */ int dir; /* usable directions */ }; diff --git a/src/pattern.c b/src/pattern.c index 670eff7d52..c2de92d4d2 100644 --- a/src/pattern.c +++ b/src/pattern.c @@ -316,6 +316,8 @@ struct pattern_expr *pattern_parse_expr(char **str, int *idx, char *err, int err expr->fetch = fetch; if (end != endw) { + char *err_msg; + if (!fetch->arg_mask) { p = my_strndup(str[*idx], endw - str[*idx]); if (p) { @@ -333,6 +335,16 @@ struct pattern_expr *pattern_parse_expr(char **str, int *idx, char *err, int err } goto out_error; } + + if (fetch->val_args && !fetch->val_args(expr->arg_p, &err_msg)) { + p = my_strndup(str[*idx], endw - str[*idx]); + if (p) { + snprintf(err, err_size, "invalid args in fetch method '%s' : %s.", p, err_msg); + free(p); + } + free(err_msg); + goto out_error; + } } else if (fetch->arg_mask) { p = my_strndup(str[*idx], endw - str[*idx]); @@ -393,6 +405,8 @@ struct pattern_expr *pattern_parse_expr(char **str, int *idx, char *err, int err conv_expr->conv = conv; if (end != endw) { + char *err_msg; + if (!conv->arg_mask) { p = my_strndup(str[*idx], endw - str[*idx]); @@ -411,6 +425,16 @@ struct pattern_expr *pattern_parse_expr(char **str, int *idx, char *err, int err } goto out_error; } + + if (conv->val_args && !conv->val_args(conv_expr->arg_p, &err_msg)) { + p = my_strndup(str[*idx], endw - str[*idx]); + if (p) { + snprintf(err, err_size, "invalid args in conv method '%s' : %s.", p, err_msg); + free(p); + } + free(err_msg); + goto out_error; + } } else if (conv->arg_mask) { p = my_strndup(str[*idx], endw - str[*idx]); @@ -505,9 +529,9 @@ static int pattern_conv_ipmask(const struct arg *arg_p, union pattern_data *data /* Note: must not be declared as its list will be overwritten */ static struct pattern_conv_kw_list pattern_conv_kws = {{ },{ - { "upper", pattern_conv_str2upper, 0, PATTERN_TYPE_STRING, PATTERN_TYPE_STRING }, - { "lower", pattern_conv_str2lower, 0, PATTERN_TYPE_STRING, PATTERN_TYPE_STRING }, - { "ipmask", pattern_conv_ipmask, ARG1(1,MSK4), PATTERN_TYPE_IP, PATTERN_TYPE_IP }, + { "upper", pattern_conv_str2upper, 0, NULL, PATTERN_TYPE_STRING, PATTERN_TYPE_STRING }, + { "lower", pattern_conv_str2lower, 0, NULL, PATTERN_TYPE_STRING, PATTERN_TYPE_STRING }, + { "ipmask", pattern_conv_ipmask, ARG1(1,MSK4), NULL, PATTERN_TYPE_IP, PATTERN_TYPE_IP }, { NULL, NULL, 0, 0, 0 }, }}; diff --git a/src/proto_http.c b/src/proto_http.c index 7809b7adab..f099913ad9 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -8533,16 +8533,15 @@ pattern_fetch_set_cookie(struct proxy *px, struct session *l4, void *l7, int dir return found; } - /************************************************************************/ /* All supported keywords must be declared here. */ /************************************************************************/ /* Note: must not be declared as its list will be overwritten */ static struct pattern_fetch_kw_list pattern_fetch_keywords = {{ },{ - { "hdr", pattern_fetch_hdr, ARG1(1,STR), PATTERN_TYPE_STRING, PATTERN_FETCH_REQ }, - { "url_param", pattern_fetch_url_param, ARG1(1,STR), PATTERN_TYPE_STRING, PATTERN_FETCH_REQ }, - { "cookie", pattern_fetch_cookie, ARG1(1,STR), PATTERN_TYPE_STRING, PATTERN_FETCH_REQ }, - { "set-cookie", pattern_fetch_set_cookie, ARG1(1,STR), PATTERN_TYPE_STRING, PATTERN_FETCH_RTR }, + { "hdr", pattern_fetch_hdr, ARG1(1,STR), NULL, PATTERN_TYPE_STRING, PATTERN_FETCH_REQ }, + { "url_param", pattern_fetch_url_param, ARG1(1,STR), NULL, PATTERN_TYPE_STRING, PATTERN_FETCH_REQ }, + { "cookie", pattern_fetch_cookie, ARG1(1,STR), NULL, PATTERN_TYPE_STRING, PATTERN_FETCH_REQ }, + { "set-cookie", pattern_fetch_set_cookie, ARG1(1,STR), NULL, PATTERN_TYPE_STRING, PATTERN_FETCH_RTR }, { NULL, NULL, 0, 0, 0 }, }}; diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 52d5cafa4c..715f1e9068 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -1493,6 +1493,50 @@ pattern_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir return 1; } +/* This function is used to validate the arguments passed to a "payload" fetch + * keyword. This keyword expects two positive integers, with the second one + * being strictly positive. It is assumed that the types are already the correct + * ones. Returns 0 on error, non-zero if OK. If is not NULL, it will be + * filled with a pointer to an error message in case of error, that the caller + * is responsible for freeing. The initial location must either be freeable or + * NULL. + */ +static int val_payload(struct arg *arg, char **err_msg) +{ + if (!arg[1].data.uint) { + if (err_msg) + memprintf(err_msg, "payload length must be > 0"); + return 0; + } + return 1; +} + +/* This function is used to validate the arguments passed to a "payload_lv" fetch + * keyword. This keyword allows two positive integers and an optional signed one, + * with the second one being strictly positive and the third one being greater than + * the opposite of the two others if negative. It is assumed that the types are + * already the correct ones. Returns 0 on error, non-zero if OK. If is + * not NULL, it will be filled with a pointer to an error message in case of + * error, that the caller is responsible for freeing. The initial location must + * either be freeable or NULL. + */ +static int val_payload_lv(struct arg *arg, char **err_msg) +{ + if (!arg[1].data.uint) { + if (err_msg) + memprintf(err_msg, "payload length must be > 0"); + return 0; + } + + if (arg[2].type == ARGT_SINT && + (int)(arg[0].data.uint + arg[1].data.uint + arg[2].data.sint) < 0) { + if (err_msg) + memprintf(err_msg, "payload offset too negative"); + return 0; + } + return 1; +} + static struct cfg_kw_list cfg_kws = {{ },{ { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req }, { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep }, @@ -1512,14 +1556,14 @@ static struct acl_kw_list acl_kws = {{ },{ /* Note: must not be declared as its list will be overwritten */ static struct pattern_fetch_kw_list pattern_fetch_keywords = {{ },{ - { "src", pattern_fetch_src, 0, PATTERN_TYPE_IP, PATTERN_FETCH_REQ }, - { "src6", pattern_fetch_src6, 0, PATTERN_TYPE_IPV6, PATTERN_FETCH_REQ }, - { "dst", pattern_fetch_dst, 0, PATTERN_TYPE_IP, PATTERN_FETCH_REQ }, - { "dst6", pattern_fetch_dst6, 0, PATTERN_TYPE_IPV6, PATTERN_FETCH_REQ }, - { "dst_port", pattern_fetch_dport, 0, PATTERN_TYPE_INTEGER, PATTERN_FETCH_REQ }, - { "payload", pattern_fetch_payload, ARG2(2,UINT,UINT), PATTERN_TYPE_CONSTDATA, PATTERN_FETCH_REQ|PATTERN_FETCH_RTR }, - { "payload_lv", pattern_fetch_payloadlv, ARG3(2,UINT,UINT,SINT), PATTERN_TYPE_CONSTDATA, PATTERN_FETCH_REQ|PATTERN_FETCH_RTR }, - { "rdp_cookie", pattern_fetch_rdp_cookie, ARG1(1,STR), PATTERN_TYPE_CONSTSTRING, PATTERN_FETCH_REQ }, + { "src", pattern_fetch_src, 0, NULL, PATTERN_TYPE_IP, PATTERN_FETCH_REQ }, + { "src6", pattern_fetch_src6, 0, NULL, PATTERN_TYPE_IPV6, PATTERN_FETCH_REQ }, + { "dst", pattern_fetch_dst, 0, NULL, PATTERN_TYPE_IP, PATTERN_FETCH_REQ }, + { "dst6", pattern_fetch_dst6, 0, NULL, PATTERN_TYPE_IPV6, PATTERN_FETCH_REQ }, + { "dst_port", pattern_fetch_dport, 0, NULL, PATTERN_TYPE_INTEGER, PATTERN_FETCH_REQ }, + { "payload", pattern_fetch_payload, ARG2(2,UINT,UINT), val_payload, PATTERN_TYPE_CONSTDATA, PATTERN_FETCH_REQ|PATTERN_FETCH_RTR }, + { "payload_lv", pattern_fetch_payloadlv, ARG3(2,UINT,UINT,SINT), val_payload_lv, PATTERN_TYPE_CONSTDATA, PATTERN_FETCH_REQ|PATTERN_FETCH_RTR }, + { "rdp_cookie", pattern_fetch_rdp_cookie, ARG1(1,STR), NULL, PATTERN_TYPE_CONSTSTRING, PATTERN_FETCH_REQ }, { NULL, NULL, 0, 0, 0 }, }};