How Exim's ${run ...}
string expansion operator does quoting
Exim has a complicated string expansion system
with various expansion operations. One of these is ${run}
, which
runs a command to get its output (or just its exit status if you
only care about that). The documentation for ${run}
says, in part:
- ${run{<command> <args>}{<string1>}{<string2>}}
- The command and its arguments are first expanded as one string. The string is split apart into individual arguments by spaces, [...]
Since the arguments are split by spaces, when there is a variable expansion which has an empty result, it will cause the situation that the argument will simply be omitted when the program is actually executed by Exim. If the script/program requires a specific number of arguments and the expanded variable could possibly result in this empty expansion, the variable must be quoted. [...]
What this documentation does not say is just how the command line is supposed to be quoted. For reasons to be covered later I have recently become extremely interested in this question, so I now have some answers.
The short answer is that the command is interpreted in the same way as it is in the pipe transport. Specifically:
Unquoted arguments are delimited by white space. If an argument appears in double quotes, backslash is interpreted as an escape character in the usual way. If an argument appears in single quotes, no escaping is done.
The usual way that backslashed escape sequences are handled is covered in character escape sequences in expanded strings.
Although the documentation for ${run}
suggests using the sg
operator to substitute dangerous characters, it appears that the
much better approach is to use the quote operator instead.
Using quote is simple and will allow you to pass through arguments
unchanged, instead of either mangling characters with sg or
doing complicated insertions of backslashes and so on. Note that
this 'passing through unchanged' will include passing through literal
newlines, which may be something you have to guard against in the
command you're running. In fact, it appears that almost any time
you're putting an Exim variable into a ${run}
command line you
should slap a ${quote:...}
around it. Maybe the variable can't
have whitespace or other dangerous things in it, but why take the
chance?
(I suspect that the ${run}
documentation was written at a time
that quote didn't exist, but I haven't checked this.)
This documentation situation is less than ideal, to put it one way.
It's possible that you can work all of this out without reading the
Exim source code if you read all of the documentation at once and
can hold it all in your head, but that's often not how documentation
is used; instead it gets consulted as a sporadic reference. The
${run}
writeup should at least have pointers to the sections with
specific information on quoting, and ideally would have at least a
brief inline discussion of its quoting rules.
(I also believe that the rules surrounding ${run}
's handling of
argument expansion are dangerous and wrong, but it's too late to
fix them now. See this entry
and also this one.)
Sidebar: where in the Exim source this is
Since I had to read the Exim source to get my answer, I might as well note down where I found things.
${run}
itself is handled in the EITEM_RUN
case in
expand_string_internal
in expand.c. The actual command handling
is done by calling transport_set_up_command
, which is in
transport.c. This handles single quotes itself in inline code but
defers double quote handling to string_dequote
in string.c,
which calls string_interpret_escape
to handle backslashed
escape sequences.
(It looks like transport_set_up_command
is called by various
different things under various circumstances that I'm not going
to try to decode the Exim source code to nail down.)
|
|