<br><tt><font size=2>Eric Blake <eblake@redhat.com> wrote on 09/27/2010
04:52:31 PM:<br>
</font></tt>
<br><tt><font size=2>> <br>
> On 09/27/2010 12:40 PM, Stefan Berger wrote:<br>
> > V2 changes:<br>
> >   following Eric's comments:<br>
> >    - commands in the script are assigned using cmd='...'
rather <br>
> than cmd="..."<br>
> >    - invoke commands using eval res=... rather than
res=eval ...<br>
> >    - rewrote function escaping ` and single quotes
(which became <br>
> more tricky)<br>
> ><br>
> > In this patch I am extending the rule instantiator to create
the comment<br>
> > node where supported, which is the case for iptables and ip6tables.<br>
> ><br>
> > Since commands are written in the format<br>
> ><br>
> > cmd='iptables ...-m comment --comment \"\" '<br>
> ><br>
> > certain characters ('`) in the comment need to be escaped to<br>
> > prevent comments from becoming commands themselves or cause other<br>
> > forms of (bash) substitutions. I have tested this with various
input and in<br>
> > my tests the input made it straight into the comment. A test
case for TCK<br>
> > will be provided separately that tests this.<br>
> <br>
> At first, I failed to see how ` needs escaping inside '', unless you
<br>
> aren't uniformly using '' like you think you are.  Then it hit
me - yuck <br>
> - we aren't uniformly using '' - we really do have to escape ` inside
<br>
> ``, or come up with an alternate solution.  That is:<br>
> <br>
> ret=`iptables -m comment --comment '`'`<br>
> <br>
> is indeed ill-formed.  But if you are going to escape `, then
you also <br>
> have to escape \ and $ for the same duration.  Yuck again.</font></tt>
<br>
<br>
<br><tt><font size=2>I had tested these also and it doesn't seem to be
the case. I looked like really only ` and ' needed special treatment.</font></tt>
<br>
<br>
<br><tt><font size=2>> <br>
> But fear not - I have a slicker solution:<br>
> <br>
> comment='comment with lone '\'', ", `, \, $x, and two  spaces'<br>
> cmd='iptables ...-m comment --comment "$comment"'<br>
> eval ret=\`"$cmd"\`<br>
</font></tt>
<br>
<br><tt><font size=2>I changed this now for v3.</font></tt>
<br>
<br>
<br><tt><font size=2>> <br>
> which at expansion time results in:<br>
> <br>
> eval ret=\`"$cmd"\`<br>
> ret=`iptables ...-m comment --comment "$comment"`<br>
> iptables ...-m comment --comment \<br>
>   'comment with lone '\'', `, ", `, \, $x, and two  spaces'<br>
> <br>
> That is, rather than trying to pass the comment literally through
$cmd <br>
> (and thus having to carefully escape $cmd for its eventual use inside
<br>
> ``), it might be nicer to stick the comment in an intermediate variable
<br>
> (where you only need to escape ') and make $cmd reference the <br>
> intermediate variable (where you won't have any problematic uses of
", <br>
> `, or ' to contend with, and where your only $ is one where you <br>
> intentionally want variable expansion).</font></tt>
<br>
<br>
<br><tt><font size=2>It's nicer, true, just a little more changes need
for building the final command where </font></tt>
<br><tt><font size=2>a 'prefix', here the comment='' is stuck in front
of the line with the iptables command.</font></tt>
<br>
<br><tt><font size=2><br>
> <br>
> > @@ -52,10 +53,10 @@<br>
> ><br>
> ><br>
> >  #define CMD_SEPARATOR "\n"<br>
> > -#define CMD_DEF_PRE  "cmd=\""<br>
> > -#define CMD_DEF_POST "\""<br>
> > +#define CMD_DEF_PRE  "cmd='"<br>
> > +#define CMD_DEF_POST "'"<br>
> >  #define CMD_DEF(X) CMD_DEF_PRE X CMD_DEF_POST<br>
> > -#define CMD_EXEC   "res=`${cmd}`" CMD_SEPARATOR<br>
> > +#define CMD_EXEC   "eval res=\\`\"${cmd}\"\\`"
CMD_SEPARATOR<br>
> <br>
> This part seems okay - the ` is quoted to protect it from evaluation
<br>
> until after eval has had a chance to collect its arguments.<br>
> <br>
> > +static char *<br>
> > +escapeComment(const char *buf)<br>
> > +{<br>
> > +    char *res;<br>
> > +    size_t i, j, add = 0, len = strlen(buf);<br>
> > +<br>
> > +    static const char SINGLEQUOTE_REPLACEMENT[12]
= "'\\'\\\"\\'\\\"\\''";<br>
> <br>
> That seems rather long to me.  Broken into chunks with C-string
escaping <br>
> eliminated:<br>
> <br>
> '    \'\"\'\"\'    '<br>
> end the current single quoting<br>
>       the 5 literal shell chars '"'"'<br>
>                    
resume single quoting<br>
> <br>
> I'm not following why we need 5 literal shell characters, instead
of 1.<br>
> </font></tt>
<br><tt><font size=2>That's because I needed to place the ' in between
"". Without it would not work. The first ' was to close the string
in front and the final ' was to start another string.</font></tt>
<br><tt><font size=2><br>
> > +<br>
> > +    if (len > IPTABLES_MAX_COMMENT_SIZE)<br>
> > +        len = IPTABLES_MAX_COMMENT_SIZE;<br>
> > +<br>
> > +    for (i = 0; i < len; i++) {<br>
> > +        if (buf[i] == '`')<br>
> > +            add++;<br>
> > +        else if (buf[i] == '\'')<br>
> > +            add += sizeof(SINGLEQUOTE_REPLACEMENT);<br>
> > +    }<br>
> <br>
> Back to my observation that this doesn't help for \ or $, the other
two <br>
> characters that need escaping inside ``.  It would be so much
easier if <br>
> we could rely on $() instead of ``.  Wait a minute - this is
only done <br>
> for Linux hosts, where we are guaranteed a sane shell (it's pretty
much <br>
> just Solaris where /bin/sh is too puny to do $()) - using $() instead
of <br>
> `` would solve a lot of escaping issues.<br>
> <br>
> > @@ -993,6 +1034,16 @@ iptablesHandleIpHdr(virBufferPtr buf,<br>
> >          }<br>
> >      }<br>
> ><br>
> > +    if (HAS_ENTRY_ITEM(&ipHdr->dataComment))
{<br>
> > +        char *cmt = escapeComment(ipHdr->dataComment.u.string);<br>
> > +        if (!cmt)<br>
> > +            goto err_exit;<br>
> > +        virBufferVSprintf(buf,<br>
> > +                  
       " -m comment --comment '\\''%s'\\''",<br>
> > +                  
       cmt);<br>
> > +        VIR_FREE(cmt);<br>
> <br>
> OK, maybe I see why your comment had such a long replacement for ',
<br>
> because you aren't adding any escaping to cmt here.  But I still
think <br>
> we can come up with a more elegant solution, by using the intermediate
<br>
> variable.  Thanks for forcing me to explain myself - it's an
interesting <br>
> process thinking about this.<br>
> <br>
> <br>
> [Do I even dare mention that at an even higher layer, it might be
nicer <br>
> to avoid /bin/sh in the first place, and instead put effort into my
<br>
> pending virCommand API patches to make it easier to directly invoke
all <br>
> the iptables commands from C?]</font></tt>
<br>
<br><tt><font size=2>I do also generate some scripts with 'if .. then'
statements that won't be able to executed via your virCommand API... </font></tt>
<br>
<br><tt><font size=2>   Stefan</font></tt>
<br>
<br>