Previously, files included with the acl <..> -f <filename> construct would
open <filename> relative to the current directory. This would mean that
( cd /tmp ; haproxy -d -f /etc/haproxy/haprocy.cfg ) and
( cd /etc/haproxy ; haproxy -d -f /etc/haproxy/haprocy.cfg )
could give different results if the configuration file had
any relative pathnames in acl -f constructs.
Change this to always open included files relative to the
configuration file they are being included from.
---
include/proto/acl.h | 8 ++++--
src/acl.c | 52 +++++++++++++++++++++++++++++++++++++++++---------
src/cfgparse.c | 2 +-
3 files changed, 48 insertions(+), 14 deletions(-)
diff --git a/include/proto/acl.h b/include/proto/acl.h
index c35cee9..c5f412f 100644
--- a/include/proto/acl.h
+++ b/include/proto/acl.h
@@ -57,8 +57,9 @@ struct acl_keyword *find_acl_kw(const char *kw);
/* Parse an ACL expression starting at <args>[0], and return it.
* Right now, the only accepted syntax is :
* <subject> [<value>...]
+ * The ACL is defined in the configuration file <configfile>
*/
-struct acl_expr *parse_acl_expr(const char **args);
+struct acl_expr *parse_acl_expr(const char *configfile, const char **args);
/* Purge everything in the acl <acl>, then return <acl>. */
struct acl *prune_acl(struct acl *acl);
@@ -66,10 +67,11 @@ struct acl *prune_acl(struct acl *acl);
/* Parse an ACL with the name starting at <args>[0], and with a list of already
* known ACLs in <acl>. If the ACL was not in the list, it will be added.
* A pointer to that ACL is returned.
+ * The ACL is defined in the file <filename>.
*
* args syntax: <aclname> <acl_expr>
*/
-struct acl *parse_acl(const char **args, struct list *known_acl);
+struct acl *parse_acl(const char *filename, const char **args, struct list
*known_acl);
/* Purge everything in the acl_cond <cond>, then return <cond>. */
struct acl_cond *prune_acl_cond(struct acl_cond *cond);
@@ -78,7 +80,7 @@ struct acl_cond *prune_acl_cond(struct acl_cond *cond);
* known ACLs passed in <known_acl>. The new condition is returned (or NULL in
* case of low memory). Supports multiple conditions separated by "or".
*/
-struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int
pol);
+struct acl_cond *parse_acl_cond(const char *configfile, const char **args,
struct list *known_acl, int pol);
/* Builds an ACL condition starting at the if/unless keyword. The complete
* condition is returned. NULL is returned in case of error or if the first
diff --git a/src/acl.c b/src/acl.c
index 9d9a746..f85f0d0 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -1053,7 +1053,36 @@ static struct acl_expr *prune_acl_expr(struct acl_expr
*expr)
return expr;
}
-static int acl_read_patterns_from_file( struct acl_keyword *aclkw,
+/* Return the full pathname of <filename> relative to <basefile>.
+ * If there is any kind of problem, just return <filename>.
+ *
+ * <buffer> may be used to construct the result, and
+ * up to <buflen> bytes will be used. If the result does not fit,
+ * <filename> will be returned instead.
+ */
+static const char *combine_paths(const char *basefile, const char *filename,
char *buffer, size_t buflen)
+{
+ const char *last_slash;
+ size_t baselen;
+
+ if (!basefile || filename[0] == '/')
+ return filename;
+
+ last_slash = strrchr(basefile, '/');
+ if (!last_slash)
+ return filename;
+
+ baselen = last_slash + 1 - basefile;
+ if (baselen + strlen(filename) + 1 > buflen)
+ return filename;
+
+ memcpy(buffer, basefile, baselen);
+ memcpy(buffer + baselen, filename, strlen(filename) + 1);
+ return buffer;
+}
+
+static int acl_read_patterns_from_file( const char *configfile,
+ struct acl_keyword *aclkw,
struct acl_expr *expr,
const char *filename, int patflags)
{
@@ -1063,8 +1092,9 @@ static int acl_read_patterns_from_file( struct
acl_keyword *aclkw,
struct acl_pattern *pattern;
int opaque;
int ret = 0;
+ char buffer[MAXPATHLEN];
- file = fopen(filename, "r");
+ file = fopen(combine_paths(configfile, filename, buffer, sizeof
buffer), "r");
if (!file)
return 0;
@@ -1137,7 +1167,7 @@ static int acl_read_patterns_from_file( struct
acl_keyword *aclkw,
* Right now, the only accepted syntax is :
* <subject> [<value>...]
*/
-struct acl_expr *parse_acl_expr(const char **args)
+struct acl_expr *parse_acl_expr(const char *configfile, const char **args)
{
__label__ out_return, out_free_expr, out_free_pattern;
struct acl_expr *expr;
@@ -1188,7 +1218,7 @@ struct acl_expr *parse_acl_expr(const char **args)
if ((*args)[1] == 'i')
patflags |= ACL_PAT_F_IGNORE_CASE;
else if ((*args)[1] == 'f') {
- if (!acl_read_patterns_from_file(aclkw, expr, args[1],
patflags | ACL_PAT_F_FROM_FILE))
+ if (!acl_read_patterns_from_file(configfile, aclkw,
expr, args[1], patflags | ACL_PAT_F_FROM_FILE))
goto out_free_expr;
args++;
}
@@ -1249,9 +1279,11 @@ struct acl *prune_acl(struct acl *acl) {
* A pointer to that ACL is returned. If the ACL has an empty name, then it's
* an anonymous one and it won't be merged with any other one.
*
+ * The ACL is defined in the file <file> - required for parsing "-f" constructs
+ *
* args syntax: <aclname> <acl_expr>
*/
-struct acl *parse_acl(const char **args, struct list *known_acl)
+struct acl *parse_acl(const char *file, const char **args, struct list
*known_acl)
{
__label__ out_return, out_free_acl_expr, out_free_name;
struct acl *cur_acl;
@@ -1261,7 +1293,7 @@ struct acl *parse_acl(const char **args, struct list
*known_acl)
if (**args && invalid_char(*args))
goto out_return;
- acl_expr = parse_acl_expr(args + 1);
+ acl_expr = parse_acl_expr(file, args + 1);
if (!acl_expr)
goto out_return;
@@ -1357,7 +1389,7 @@ struct acl *find_acl_default(const char *acl_name, struct
list *known_acl)
if (default_acl_list[index].name == NULL)
return NULL;
- acl_expr = parse_acl_expr((const char **)default_acl_list[index].expr);
+ acl_expr = parse_acl_expr(NULL, (const char
**)default_acl_list[index].expr);
if (!acl_expr)
goto out_return;
@@ -1405,7 +1437,7 @@ struct acl_cond *prune_acl_cond(struct acl_cond *cond)
* known ACLs passed in <known_acl>. The new condition is returned (or NULL in
* case of low memory). Supports multiple conditions separated by "or".
*/
-struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int
pol)
+struct acl_cond *parse_acl_cond(const char *configfile, const char **args,
struct list *known_acl, int pol)
{
__label__ out_return, out_free_suite, out_free_term;
int arg, neg;
@@ -1467,7 +1499,7 @@ struct acl_cond *parse_acl_cond(const char **args, struct
list *known_acl, int p
args_new[0] = "";
memcpy(args_new + 1, args + arg + 1, (arg_end - arg) *
sizeof(*args_new));
args_new[arg_end - arg] = "";
- cur_acl = parse_acl(args_new, known_acl);
+ cur_acl = parse_acl(configfile, args_new, known_acl);
free(args_new);
if (!cur_acl)
@@ -1540,7 +1572,7 @@ struct acl_cond *build_acl_cond(const char *file, int
line, struct proxy *px, co
else
return NULL;
- cond = parse_acl_cond(args, &px->acl, pol);
+ cond = parse_acl_cond(file, args, &px->acl, pol);
if (!cond)
return NULL;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 5b942e2..34937b0 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2020,7 +2020,7 @@ int cfg_parse_listen(const char *file, int linenum, char
**args, int kwm)
err_code |= ERR_ALERT | ERR_FATAL;
}
- if (parse_acl((const char **)args + 1, &curproxy->acl) == NULL)
{
+ if (parse_acl(file, (const char **)args + 1, &curproxy->acl) ==
NULL) {
Alert("parsing [%s:%d] : error detected while parsing
ACL '%s'.\n",
file, linenum, args[1]);
err_code |= ERR_ALERT | ERR_FATAL;
--
1.7.5.1.217.g4e3aa