On Thu, Nov 05, 2015 at 08:54:39AM +0100, Lukas Tribus wrote:
> >> @@ -402,7 +402,7 @@ smp_fetch_req_ssl_ver(const struct arg *args, struct
> >> sample *smp, const char *kw
> >> if (bleft < 5)
> >> goto too_short;
> >>
> >> - version = (data[1] << 16) + data[2]; /* version: major, minor */
> >> + version = (data[9] << 16) + data[10]; /* client hello version: major,
> >> minor */
> >> msg_len = (data[3] << 8) + data[4]; /* record length */
> >
> > See above ? we check for 5 bytes minimum because we didn't parse further
> > than data[4], and now we're reading data[10], so the test on bleft above
> > must be changed to bleft < 11.
> >
> > Can someone please check if the other patch referenced above has the same
> > bug?
>
> Ouch, here's the original patch:
> http://marc.info/?l=haproxy&m=144431273015849&w=2
>
> It does indeed bump this check to 11 bytes. Also, there is a third change in
> that path, where it touches bleft and data values (bleft -= 11; data += 11;)
> some lines below.
>
> The original patch does not work, at this point I'm not quite sure why.
It's because of bleft -= 11 and data += 11. At the end of the code there's
a comment and a check :
/* We could recursively check that the buffer ends exactly on an SSL
* fragment boundary and that a possible next segment is still SSL,
* but that's a bit pointless. However, we could still check that
* all the part of the request which fits in a buffer is already
* there.
*/
if (msg_len > channel_recv_limit(req) + req->buf->data - req->buf->p)
msg_len = channel_recv_limit(req) + req->buf->data -
req->buf->p;
msg_len is the length of what starts at byte 5. So by skipping 6 extra bytes
the msg_len becomes incorrect and this check doesn't match anymore.
... which leads to another point. Since we're checking inside a record, are
we sure that we don't need to check the record type ? There are probably
bytes between the record length and the record's version.
I'd suggest at minima that the comment on "version" is adjusted and that the
version is read *after* msg_len so that we parse the message from left to
right only to avoid any confusion :
- version = (data[1] << 16) + data[2]; /* version: major, minor */
msg_len = (data[3] << 8) + data[4]; /* record length */
+ version = (data[9] << 16) + data[10]; /* record version: major,
minor */
And in fact I'm still not satisfied with this because the check for the
message length is performed a few lines later so we may parse crap from
a subsequent record. Thus the proper thing to do is this (sorry for hand-
written patch, I think you'll get the idea) :
/* SSLv3 header format */
- if (bleft < 5)
+ if (bleft < 11)
goto too_short;
version = (data[1] << 16) + data[2]; /* version: major, minor */
msg_len = (data[3] << 8) + data[4]; /* record length */
/* format introduced with SSLv3 */
if (version < 0x00030000)
goto not_ssl;
+ /* message length between 6 and 2^14 + 2048 */
+ if (msg_len < 6 || msg_len > ((1<<14) + 2048))
+ goto not_ssl;
bleft -= 5; data += 5;
/* we want to return the record version, not the envelope's
version */
+ version = (data[4] << 16) + data[5]; /* record version: major,
minor */
> Thanks and sorry for the near-miss,
No worries, that's exactly why I want to see patches posted to the list and
why I'm strongly against pull requests. I want to see eyes looking at the
code. There are 2000 eyeballs on this list, anyone at any moment can chime
in and ask a question or report a doubt.
Thanks,
Willy