In this patch we introduce a new helped function called 
`smp_client_hello_parse()` to extract
information presented in a TLS client hello handshake message. 7 sample fetches 
have also been
modified to use this helped function to do the common client hello parsing and 
use the result
to do further processing of extensions/cipher.

Fixes: #2532
---
 src/payload.c | 645 ++++++++++++++------------------------------------
 1 file changed, 174 insertions(+), 471 deletions(-)

diff --git a/src/payload.c b/src/payload.c
index 04cc3d50e..83c6e0823 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -11,6 +11,7 @@
  */
 
 #include <stdlib.h>
+#include <stdbool.h>
 #include <string.h>
 
 #include <haproxy/acl.h>
@@ -31,57 +32,51 @@
 /*       All supported sample fetch functions must be declared here     */
 /************************************************************************/
 
-/* wait for more data as long as possible, then return TRUE. This should be
- * used with content inspection.
- */
-static int
-smp_fetch_wait_end(const struct arg *args, struct sample *smp, const char *kw, 
void *private)
-{
-       if (!(smp->opt & SMP_OPT_FINAL)) {
-               smp->flags |= SMP_F_MAY_CHANGE;
-               return 0;
-       }
-       smp->data.type = SMP_T_BOOL;
-       smp->data.u.sint = 1;
-       return 1;
-}
-
-/* return the number of bytes in the request buffer */
-static int
-smp_fetch_len(const struct arg *args, struct sample *smp, const char *kw, void 
*private)
-{
-       if (smp->strm) {
-               struct channel *chn = ((smp->opt & SMP_OPT_DIR) == 
SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
-
-               /* Not accurate but kept for backward compatibility purpose */
-               if (IS_HTX_STRM(smp->strm)) {
-                       struct htx *htx = htxbuf(&chn->buf);
-                       smp->data.u.sint = htx->data - co_data(chn);
-               }
-               else
-                       smp->data.u.sint = ci_data(chn);
-       }
-       else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK) {
-               struct check *check = __objt_check(smp->sess->origin);
+enum client_hello_status {
+       CLIENTHELLO_ERR_OK = 0,
+       CLIENTHELLO_ERR_UNAVAIL = 1,
+       CLIENTHELLO_ERR_TOO_SHORT = 2,
+};
 
-               /* Not accurate but kept for backward compatibility purpose */
-               smp->data.u.sint = ((check->sc && IS_HTX_SC(check->sc)) ? 
(htxbuf(&check->bi))->data: b_data(&check->bi));
-       }
-       else
-               return 0;
-
-       smp->data.type = SMP_T_SINT;
-       smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
-       return 1;
-}
+enum client_hello_type {
+       CLIENTHELLO_EXTENSIONS,
+       CLIENTHELLO_CIPHERSUITE,
+};
 
-/* Returns 0 if the client didn't send a SessionTicket Extension
- * Returns 1 if the client sent SessionTicket Extension
- * Returns 2 if the client also sent non-zero length SessionTicket
- * Returns SMP_T_SINT data type
+/* Extract information presented in a TLS client hello handshake message.
+ * The format of the message is the following (cf RFC5246 + RFC6066) :
+ * TLS frame :
+ *   - uint8  type                            = 0x16   (Handshake)
+ *   - uint16 version                        >= 0x0301 (TLSv1)
+ *   - uint16 length                                   (frame length)
+ *   - TLS handshake :
+ *     - uint8  msg_type                      = 0x01   (ClientHello)
+ *     - uint24 length                                 (handshake message 
length)
+ *     - ClientHello :
+ *       - uint16 client_version             >= 0x0301 (TLSv1)
+ *       - uint8 Random[32]                  (4 first ones are timestamp)
+ *       - SessionID :
+ *         - uint8 session_id_len (0..32)              (SessionID len in bytes)
+ *         - uint8 session_id[session_id_len]
+ *       - CipherSuite :
+ *         - uint16 cipher_len               >= 2      (Cipher length in bytes)
+ *         - uint16 ciphers[cipher_len/2]
+ *       - CompressionMethod :
+ *         - uint8 compression_len           >= 1      (# of supported methods)
+ *         - uint8 compression_methods[compression_len]
+ *       - optional client_extension_len               (in bytes)
+ *       - optional sequence of ClientHelloExtensions  (as many bytes as 
above):
+ *         - uint16 extension_type            = 0 for server_name
+ *         - uint16 extension_len
+ *         - opaque extension_data[extension_len]
+ *           - uint16 server_name_list_len             (# of bytes here)
+ *           - opaque server_names[server_name_list_len bytes]
+ *             - uint8 name_type              = 0 for host_name
+ *             - uint16 name_len
+ *             - opaque hostname[name_len bytes]
  */
 static int
-smp_fetch_req_ssl_st_ext(const struct arg *args, struct sample *smp, const 
char *kw, void *private)
+smp_client_hello_parse( struct sample *smp, enum client_hello_type type, 
unsigned char **ch_data, int *len)
 {
        int hs_len, ext_len, bleft;
        struct channel *chn;
@@ -156,24 +151,109 @@ smp_fetch_req_ssl_st_ext(const struct arg *args, struct 
sample *smp, const char
            ext_len > hs_len)
                goto not_ssl_hello;
 
-       /* Jump to the compression methods */
-       hs_len -= 2 + ext_len;
-       data   += 2 + ext_len;
+       /* Jump to the compression methods. For fetching cipher list this 
processing is not required. */
+       if (type == CLIENTHELLO_EXTENSIONS)
+               goto parse_extn;
+       else
+               goto parse_cipher;
 
-       if (hs_len < 2 ||                       /* minimum one compression 
method */
-           data[0] < 1 || data[0] > hs_len)    /* minimum 1 bytes for a method 
*/
-               goto not_ssl_hello;
+       parse_extn:
+               hs_len -= 2 + ext_len;
+               data   += 2 + ext_len;
 
-       /* Jump to the extensions */
-       hs_len -= 1 + data[0];
-       data   += 1 + data[0];
+               if (hs_len < 2 ||                       /* minimum one 
compression method */
+                   data[0] < 1 || data[0] > hs_len)    /* minimum 1 bytes for 
a method */
+                       goto not_ssl_hello;
 
-       if (hs_len < 2 ||                       /* minimum one extension list 
length */
-           (ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list too long 
*/
+               /* Jump to the extensions */
+               hs_len -= 1 + data[0];
+               data   += 1 + data[0];
+
+               if (hs_len < 2 ||                      /* minimum one extension 
list length */
+                   (ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list 
too long */
+                       goto not_ssl_hello;
+
+               hs_len = ext_len; /* limit ourselves to the extension length */
+               data += 2;
+
+               *len = hs_len;
+               *ch_data = data;
+               return CLIENTHELLO_ERR_OK;
+
+       parse_cipher:
+               *len = ext_len;
+               *ch_data = data;
+               return CLIENTHELLO_ERR_OK;
+
+       not_ssl_hello:
+               return CLIENTHELLO_ERR_UNAVAIL;
+
+       too_short:
+               return CLIENTHELLO_ERR_TOO_SHORT;
+}
+
+/* wait for more data as long as possible, then return TRUE. This should be
+ * used with content inspection.
+ */
+static int
+smp_fetch_wait_end(const struct arg *args, struct sample *smp, const char *kw, 
void *private)
+{
+       if (!(smp->opt & SMP_OPT_FINAL)) {
+               smp->flags |= SMP_F_MAY_CHANGE;
+               return 0;
+       }
+       smp->data.type = SMP_T_BOOL;
+       smp->data.u.sint = 1;
+       return 1;
+}
+
+/* return the number of bytes in the request buffer */
+static int
+smp_fetch_len(const struct arg *args, struct sample *smp, const char *kw, void 
*private)
+{
+       if (smp->strm) {
+               struct channel *chn = ((smp->opt & SMP_OPT_DIR) == 
SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
+
+               /* Not accurate but kept for backward compatibility purpose */
+               if (IS_HTX_STRM(smp->strm)) {
+                       struct htx *htx = htxbuf(&chn->buf);
+                       smp->data.u.sint = htx->data - co_data(chn);
+               }
+               else
+                       smp->data.u.sint = ci_data(chn);
+       }
+       else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK) {
+               struct check *check = __objt_check(smp->sess->origin);
+
+               /* Not accurate but kept for backward compatibility purpose */
+               smp->data.u.sint = ((check->sc && IS_HTX_SC(check->sc)) ? 
(htxbuf(&check->bi))->data: b_data(&check->bi));
+       }
+       else
+               return 0;
+
+       smp->data.type = SMP_T_SINT;
+       smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
+       return 1;
+}
+
+/* Returns 0 if the client didn't send a SessionTicket Extension
+ * Returns 1 if the client sent SessionTicket Extension
+ * Returns 2 if the client also sent non-zero length SessionTicket
+ * Returns SMP_T_SINT data type
+ */
+static int
+smp_fetch_req_ssl_st_ext(const struct arg *args, struct sample *smp, const 
char *kw, void *private)
+{
+       enum client_hello_status status;
+       int hs_len;
+       unsigned char *data;
+
+       status = smp_client_hello_parse(smp, CLIENTHELLO_EXTENSIONS, &data, 
&hs_len);
+       if (status == CLIENTHELLO_ERR_UNAVAIL)
                goto not_ssl_hello;
+       else if (status == CLIENTHELLO_ERR_TOO_SHORT)
+               goto too_short;
 
-       hs_len = ext_len; /* limit ourselves to the extension length */
-       data += 2;
 
        while (hs_len >= 4) {
                int ext_type, ext_len;
@@ -219,96 +299,16 @@ smp_fetch_req_ssl_st_ext(const struct arg *args, struct 
sample *smp, const char
 static int
 smp_fetch_req_ssl_ec_ext(const struct arg *args, struct sample *smp, const 
char *kw, void *private)
 {
-       int hs_len, ext_len, bleft;
-       struct channel *chn;
+       enum client_hello_status status;
+       int hs_len;
        unsigned char *data;
 
-       if (!smp->strm)
-               goto not_ssl_hello;
-
-       /* meaningless for HTX buffers */
-       if (IS_HTX_STRM(smp->strm))
-               goto not_ssl_hello;
-
-       chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : 
&smp->strm->req;
-       bleft = ci_data(chn);
-       data = (unsigned char *)ci_head(chn);
-
-       /* Check for SSL/TLS Handshake */
-       if (!bleft)
-               goto too_short;
-       if (*data != 0x16)
-               goto not_ssl_hello;
-
-       /* Check for SSLv3 or later (SSL version >= 3.0) in the record layer*/
-       if (bleft < 3)
-               goto too_short;
-       if (data[1] < 0x03)
-               goto not_ssl_hello;
-
-       if (bleft < 5)
-               goto too_short;
-       hs_len = (data[3] << 8) + data[4];
-       if (hs_len < 1 + 3 + 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       data += 5; /* enter TLS handshake */
-       bleft -= 5;
-
-       /* Check for a complete client hello starting at <data> */
-       if (bleft < 1)
-               goto too_short;
-       if (data[0] != 0x01) /* msg_type = Client Hello */
+       status = smp_client_hello_parse(smp, CLIENTHELLO_EXTENSIONS, &data, 
&hs_len);
+       if (status == CLIENTHELLO_ERR_UNAVAIL)
                goto not_ssl_hello;
-
-       /* Check the Hello's length */
-       if (bleft < 4)
-               goto too_short;
-       hs_len = (data[1] << 16) + (data[2] << 8) + data[3];
-       if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       /* We want the full handshake here */
-       if (bleft < hs_len)
+       else if (status == CLIENTHELLO_ERR_TOO_SHORT)
                goto too_short;
 
-       data += 4;
-       /* Start of the ClientHello message */
-       if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
-               goto not_ssl_hello;
-
-       ext_len = data[34]; /* session_id_len */
-       if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct 
session_id len */
-               goto not_ssl_hello;
-
-       /* Jump to cipher suite */
-       hs_len -= 35 + ext_len;
-       data   += 35 + ext_len;
-
-       if (hs_len < 4 ||                               /* minimum one cipher */
-           (ext_len = (data[0] << 8) + data[1]) < 2 || /* minimum 2 bytes for 
a cipher */
-           ext_len > hs_len)
-               goto not_ssl_hello;
-
-       /* Jump to the compression methods */
-       hs_len -= 2 + ext_len;
-       data   += 2 + ext_len;
-
-       if (hs_len < 2 ||                       /* minimum one compression 
method */
-           data[0] < 1 || data[0] > hs_len)    /* minimum 1 bytes for a method 
*/
-               goto not_ssl_hello;
-
-       /* Jump to the extensions */
-       hs_len -= 1 + data[0];
-       data   += 1 + data[0];
-
-       if (hs_len < 2 ||                       /* minimum one extension list 
length */
-           (ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list too long 
*/
-               goto not_ssl_hello;
-
-       hs_len = ext_len; /* limit ourselves to the extension length */
-       data += 2;
-
        while (hs_len >= 4) {
                int ext_type, ext_len;
 
@@ -528,81 +528,20 @@ smp_fetch_req_ssl_ver(const struct arg *args, struct 
sample *smp, const char *kw
 static int
 smp_fetch_ssl_cipherlist(const struct arg *args, struct sample *smp, const 
char *kw, void *private)
 {
-       int hs_len, ext_len, bleft;
-       struct channel *chn;
+       enum client_hello_status status;
+       int hs_len;
        unsigned char *data;
 
-       if (!smp->strm)
+       status = smp_client_hello_parse(smp, CLIENTHELLO_CIPHERSUITE, &data, 
&hs_len);
+       if (status == CLIENTHELLO_ERR_UNAVAIL)
                goto not_ssl_hello;
+       else if (status == CLIENTHELLO_ERR_TOO_SHORT)
+               goto too_short;
 
-       /* meaningless for HTX buffers */
-       if (IS_HTX_STRM(smp->strm))
-               goto not_ssl_hello;
-
-       chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : 
&smp->strm->req;
-       bleft = ci_data(chn);
-       data = (unsigned char *)ci_head(chn);
-
-       /* Check for SSL/TLS Handshake */
-       if (!bleft)
-               goto too_short;
-       if (*data != 0x16)
-               goto not_ssl_hello;
-
-       /* Check for SSLv3 or later (SSL version >= 3.0) in the record layer*/
-       if (bleft < 3)
-               goto too_short;
-       if (data[1] < 0x03)
-               goto not_ssl_hello;
-
-       if (bleft < 5)
-               goto too_short;
-       hs_len = (data[3] << 8) + data[4];
-       if (hs_len < 1 + 3 + 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       data += 5; /* enter TLS handshake */
-       bleft -= 5;
-
-       /* Check for a complete client hello starting at <data> */
-       if (bleft < 1)
-               goto too_short;
-       if (data[0] != 0x01) /* msg_type = Client Hello */
-               goto not_ssl_hello;
-
-       /* Check the Hello's length */
-       if (bleft < 4)
-               goto too_short;
-       hs_len = (data[1] << 16) + (data[2] << 8) + data[3];
-       if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       /* We want the full handshake here */
-       if (bleft < hs_len)
-               goto too_short;
-
-       data += 4;
-       /* Start of the ClientHello message */
-       if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
-               goto not_ssl_hello;
-
-       ext_len = data[34]; /* session_id_len */
-       if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct 
session_id len */
-               goto not_ssl_hello;
-
-       /* Jump to cipher suite */
-       hs_len -= 35 + ext_len;
-       data   += 35 + ext_len;
-
-       if (hs_len < 4 ||                               /* minimum one cipher */
-           (ext_len = (data[0] << 8) + data[1]) < 2 ||  /* minimum 2 bytes for 
a cipher */
-            ext_len > hs_len)
-               goto not_ssl_hello;
-
-       smp->data.type = SMP_T_BIN;
-       smp->data.u.str.area = (char *)data + 2;
-       smp->data.u.str.data = ext_len;
-       smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
+       smp->data.type = SMP_T_BIN;
+       smp->data.u.str.area = (char *)data + 2;
+       smp->data.u.str.data = hs_len;
+       smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
 
        return 1;
 
@@ -620,97 +559,18 @@ smp_fetch_ssl_cipherlist(const struct arg *args, struct 
sample *smp, const char
 static int
 smp_fetch_ssl_supported_groups(const struct arg *args, struct sample *smp, 
const char *kw, void *private)
 {
-       int hs_len, ext_len, bleft;
-       struct channel *chn;
+       enum client_hello_status status;
+       int hs_len;
        unsigned char *data;
 
-       if (!smp->strm)
-               goto not_ssl_hello;
-
-       /* meaningless for HTX buffers */
-       if (IS_HTX_STRM(smp->strm))
-               goto not_ssl_hello;
-
-       chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : 
&smp->strm->req;
-       bleft = ci_data(chn);
-       data = (unsigned char *)ci_head(chn);
-
-       /* Check for SSL/TLS Handshake */
-       if (!bleft)
-               goto too_short;
-       if (*data != 0x16)
-               goto not_ssl_hello;
-
-       /* Check for SSLv3 or later (SSL version >= 3.0) in the record layer*/
-       if (bleft < 3)
-               goto too_short;
-       if (data[1] < 0x03)
-               goto not_ssl_hello;
-
-       if (bleft < 5)
-               goto too_short;
-       hs_len = (data[3] << 8) + data[4];
-       if (hs_len < 1 + 3 + 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       data += 5; /* enter TLS handshake */
-       bleft -= 5;
-       /* Check for a complete client hello starting at <data> */
-       if (bleft < 1)
-               goto too_short;
-       if (data[0] != 0x01) /* msg_type = Client Hello */
+       status = smp_client_hello_parse(smp, CLIENTHELLO_EXTENSIONS, &data, 
&hs_len);
+       if (status == CLIENTHELLO_ERR_UNAVAIL)
                goto not_ssl_hello;
-
-       /* Check the Hello's length */
-       if (bleft < 4)
+       else if (status == CLIENTHELLO_ERR_TOO_SHORT)
                goto too_short;
-       hs_len = (data[1] << 16) + (data[2] << 8) + data[3];
-       if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       /* We want the full handshake here */
-       if (bleft < hs_len)
-               goto too_short;
-
-       data += 4;
-       /* Start of the ClientHello message */
-       if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
-               goto not_ssl_hello;
-
-       ext_len = data[34]; /* session_id_len */
-       if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct 
session_id len */
-               goto not_ssl_hello;
-
-       /* Jump to cipher suite */
-       hs_len -= 35 + ext_len;
-       data   += 35 + ext_len;
-
-       if (hs_len < 4 ||                               /* minimum one cipher */
-           (ext_len = (data[0] << 8) + data[1]) < 2 ||  /* minimum 2 bytes for 
a cipher */
-            ext_len > hs_len)
-               goto not_ssl_hello;
-
-       /* Jump to the compression methods */
-       hs_len -= 2 + ext_len;
-       data   += 2 + ext_len;
-
-       if (hs_len < 2 ||                       /* minimum one compression 
method */
-           data[0] < 1 || data[0] > hs_len)    /* minimum 1 bytes for a method 
*/
-               goto not_ssl_hello;
-
-       /* Jump to the extensions */
-       hs_len -= 1 + data[0];
-       data   += 1 + data[0];
-
-       if (hs_len < 2 ||                       /* minimum one extension list 
length */
-           (ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list too long 
*/
-               goto not_ssl_hello;
-
-       hs_len = ext_len; /* limit ourselves to the extension length */
-       data += 2; /* Now 'data' points to the first content byte of an 
extension */
 
        while (hs_len >= 4) {
-               int ext_type, grp_len;
+               int ext_type, ext_len, grp_len;
 
                ext_type = (data[0] << 8) + data[1]; /* Extension type */
                ext_len  = (data[2] << 8) + data[3]; /* Extension length */
@@ -754,97 +614,18 @@ smp_fetch_ssl_supported_groups(const struct arg *args, 
struct sample *smp, const
 static int
 smp_fetch_ssl_sigalgs(const struct arg *args, struct sample *smp, const char 
*kw, void *private)
 {
-       int hs_len, ext_len, bleft;
-       struct channel *chn;
+       enum client_hello_status status;
+       int hs_len;
        unsigned char *data;
 
-       if (!smp->strm)
-               goto not_ssl_hello;
-
-       /* meaningless for HTX buffers */
-       if (IS_HTX_STRM(smp->strm))
-               goto not_ssl_hello;
-
-       chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : 
&smp->strm->req;
-       bleft = ci_data(chn);
-       data = (unsigned char *)ci_head(chn);
-
-       /* Check for SSL/TLS Handshake */
-       if (!bleft)
-               goto too_short;
-       if (*data != 0x16)
-               goto not_ssl_hello;
-
-       /* Check for SSLv3 or later (SSL version >= 3.0) in the record layer*/
-       if (bleft < 3)
-               goto too_short;
-       if (data[1] < 0x03)
-               goto not_ssl_hello;
-
-       if (bleft < 5)
-               goto too_short;
-       hs_len = (data[3] << 8) + data[4];
-       if (hs_len < 1 + 3 + 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       data += 5; /* enter TLS handshake */
-       bleft -= 5;
-       /* Check for a complete client hello starting at <data> */
-       if (bleft < 1)
-               goto too_short;
-       if (data[0] != 0x01) /* msg_type = Client Hello */
+       status = smp_client_hello_parse(smp, CLIENTHELLO_EXTENSIONS, &data, 
&hs_len);
+       if (status == CLIENTHELLO_ERR_UNAVAIL)
                goto not_ssl_hello;
-
-       /* Check the Hello's length */
-       if (bleft < 4)
+       else if (status == CLIENTHELLO_ERR_TOO_SHORT)
                goto too_short;
-       hs_len = (data[1] << 16) + (data[2] << 8) + data[3];
-       if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       /* We want the full handshake here */
-       if (bleft < hs_len)
-               goto too_short;
-
-       data += 4;
-       /* Start of the ClientHello message */
-       if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
-               goto not_ssl_hello;
-
-       ext_len = data[34]; /* session_id_len */
-       if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct 
session_id len */
-               goto not_ssl_hello;
-
-       /* Jump to cipher suite */
-       hs_len -= 35 + ext_len;
-       data   += 35 + ext_len;
-
-       if (hs_len < 4 ||                               /* minimum one cipher */
-                       (ext_len = (data[0] << 8) + data[1]) < 2 || /* minimum 
2 bytes for a cipher */
-                       ext_len > hs_len)
-               goto not_ssl_hello;
-
-       /* Jump to the compression methods */
-       hs_len -= 2 + ext_len;
-       data   += 2 + ext_len;
-
-       if (hs_len < 2 ||                       /* minimum one compression 
method */
-           data[0] < 1 || data[0] > hs_len)    /* minimum 1 bytes for a method 
*/
-               goto not_ssl_hello;
-
-       /* Jump to the extensions */
-       hs_len -= 1 + data[0];
-       data   += 1 + data[0];
-
-       if (hs_len < 2 ||                       /* minimum one extension list 
length */
-           (ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list too long 
*/
-               goto not_ssl_hello;
-
-       hs_len = ext_len; /* limit ourselves to the extension length */
-       data += 2; /* Now 'data' points to the first content byte of an 
extension */
 
        while (hs_len >= 4) {
-               int ext_type, sigalg_len;
+               int ext_type, ext_len, sigalg_len;
 
                ext_type = (data[0] << 8) + data[1]; /* Extension type */
                ext_len  = (data[2] << 8) + data[3]; /* Extension length */
@@ -888,100 +669,22 @@ smp_fetch_ssl_sigalgs(const struct arg *args, struct 
sample *smp, const char *kw
 static int
 smp_fetch_ssl_keyshare_groups(const struct arg *args, struct sample *smp, 
const char *kw, void *private)
 {
-       int hs_len, ext_len, bleft, readPosition, numberOfKeyshares;
-       struct channel *chn;
+       int readPosition, numberOfKeyshares;
        struct buffer *smp_trash = NULL;
        unsigned char *data;
        unsigned char *dataPointer;
+       enum client_hello_status status;
+       int hs_len;
 
-       if (!smp->strm)
-               goto not_ssl_hello;
-
-       /* meaningless for HTX buffers */
-       if (IS_HTX_STRM(smp->strm))
-               goto not_ssl_hello;
-
-       chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : 
&smp->strm->req;
-       bleft = ci_data(chn);
-       data = (unsigned char *)ci_head(chn);
-
-       /* Check for SSL/TLS Handshake */
-       if (!bleft)
-               goto too_short;
-       if (*data != 0x16)
-               goto not_ssl_hello;
-
-       /* Check for SSLv3 or later (SSL version >= 3.0) in the record layer*/
-       if (bleft < 3)
-               goto too_short;
-       if (data[1] < 0x03)
-               goto not_ssl_hello;
-
-       if (bleft < 5)
-               goto too_short;
-       hs_len = (data[3] << 8) + data[4];
-       if (hs_len < 1 + 3 + 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       data += 5; /* enter TLS handshake */
-       bleft -= 5;
 
-       /* Check for a complete client hello starting at <data> */
-       if (bleft < 1)
-               goto too_short;
-       if (data[0] != 0x01) /* msg_type = Client Hello */
+       status = smp_client_hello_parse(smp, CLIENTHELLO_EXTENSIONS, &data, 
&hs_len);
+       if (status == CLIENTHELLO_ERR_UNAVAIL)
                goto not_ssl_hello;
-
-       /* Check the Hello's length */
-       if (bleft < 4)
-               goto too_short;
-       hs_len = (data[1] << 16) + (data[2] << 8) + data[3];
-       if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       /* We want the full handshake here */
-       if (bleft < hs_len)
+       else if (status == CLIENTHELLO_ERR_TOO_SHORT)
                goto too_short;
 
-       data += 4;
-       /* Start of the ClientHello message */
-       if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
-               goto not_ssl_hello;
-
-       ext_len = data[34]; /* session_id_len */
-       if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct 
session_id len */
-               goto not_ssl_hello;
-
-       /* Jump to cipher suite */
-       hs_len -= 35 + ext_len;
-       data   += 35 + ext_len;
-
-       if (hs_len < 4 ||                               /* minimum one cipher */
-           (ext_len = (data[0] << 8) + data[1]) < 2 || /* minimum 2 bytes for 
a cipher */
-            ext_len > hs_len)
-               goto not_ssl_hello;
-
-       /* Jump to the compression methods */
-       hs_len -= 2 + ext_len;
-       data   += 2 + ext_len;
-
-       if (hs_len < 2 ||                       /* minimum one compression 
method */
-            data[0] < 1 || data[0] > hs_len)    /* minimum 1 bytes for a 
method */
-               goto not_ssl_hello;
-
-       /* Jump to the extensions */
-       hs_len -= 1 + data[0];
-       data   += 1 + data[0];
-
-       if (hs_len < 2 ||                       /* minimum one extension list 
length */
-           (ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list too long 
*/
-               goto not_ssl_hello;
-
-       hs_len = ext_len; /* limit ourselves to the extension length */
-       data += 2; /* Now 'data' points to the first content byte of an 
extension */
-
        while (hs_len >= 4) {
-               int ext_type, keyshare_len;
+               int ext_type, ext_len, keyshare_len;
 
                ext_type = (data[0] << 8) + data[1]; /* Extension type */
                ext_len  = (data[2] << 8) + data[3]; /* Extension length */
-- 
2.39.3 (Apple Git-145)



Reply via email to