]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: ssl: isolate TLS session resumption per crt-list filter
authorWilliam Lallemand <wlallemand@haproxy.com>
Tue, 18 Aug 2026 07:10:49 +0000 (07:10 +0000)
committerWilliam Lallemand <wlallemand@haproxy.com>
Tue, 18 Aug 2026 12:08:17 +0000 (14:08 +0200)
ssl_set_session_id_context() folds the frontend's own leaf certificate
identity into sid_ctx, but a crt-list can point several entries at the
exact same certificate file, distinguished only by their SNI filter,
e.g.:

    cert.pem foo.example.com
    cert.pem bar.example.com

Each entry gets its own SSL_CTX/ckch_inst, but since both share the same
certificate, they still end up with an identical sid_ctx, and a session
established while connecting to "foo.example.com" can be resumed under
"bar.example.com" (or any other filter sharing that certificate) without
the servername callback/SNI match ever running again on the resumed
handshake.

Fix the issue by adding a ckch_inst parameter in order to hash the
filters provided by the crt-list.

Must be backported in every stable branches.

src/ssl_sock.c

index 71d8cd473296689db7981dd9dc24123877f0f7ce..48af62efd7b3e15ea66b217d765101e90570281d 100644 (file)
@@ -4660,12 +4660,34 @@ static int ssl_ctx_hash_X509(EVP_MD_CTX *mdctx, X509 *cert)
        return 1;
 }
 
+/* hash SNI filters into the session ID context */
+static int ssl_ctx_hash_filters(EVP_MD_CTX *mdctx, char **filters, unsigned int fcount)
+{
+       unsigned int i;
+
+       if (EVP_DigestUpdate(mdctx, &fcount, sizeof(fcount)) != 1)
+               return 0;
+
+       for (i = 0; i < fcount; i++) {
+               size_t len = strlen(filters[i]);
+
+               /* hash the len to avoid collision like "ab""c" = "abc" */
+               if (EVP_DigestUpdate(mdctx, &len, sizeof(len)) != 1)
+                       return 0;
+               if (len && EVP_DigestUpdate(mdctx, filters[i], len) != 1)
+                       return 0;
+       }
+
+       return 1;
+}
+
 /* Replace the sid_ctx of a SSL_CTX */
-static int ssl_set_session_id_context(SSL_CTX *ctx)
+static int ssl_set_session_id_context(SSL_CTX *ctx, struct ckch_inst *ckch_inst)
 {
        EVP_MD_CTX *mdctx = NULL;
        unsigned char sid_ctx[EVP_MAX_MD_SIZE];
        unsigned int sid_ctx_len = 0;
+       struct crtlist_entry *entry = ckch_inst ? ckch_inst->crtlist_entry : NULL;
        int ret = 0;
 
        mdctx = EVP_MD_CTX_new();
@@ -4673,6 +4695,7 @@ static int ssl_set_session_id_context(SSL_CTX *ctx)
            EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL) != 1 ||
            EVP_DigestUpdate(mdctx, SHCTX_APPNAME, sizeof(SHCTX_APPNAME)) != 1 ||
            !ssl_ctx_hash_X509(mdctx, SSL_CTX_get0_certificate(ctx)) ||
+           !ssl_ctx_hash_filters(mdctx, entry ? entry->filters : NULL, entry ? entry->fcount : 0) ||
            EVP_DigestFinal_ex(mdctx, sid_ctx, &sid_ctx_len) != 1 ||
            sid_ctx_len > SSL_MAX_SID_CTX_LENGTH ||
            SSL_CTX_set_session_id_context(ctx, sid_ctx, sid_ctx_len) != 1)
@@ -4797,7 +4820,8 @@ error:
  * This function applies the SSL configuration on a SSL_CTX
  * It returns an error code and fills the <err> buffer
  */
-static int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf, SSL_CTX *ctx, char **err)
+static int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf, SSL_CTX *ctx,
+                                 struct ckch_inst *ckch_inst, char **err)
 {
        struct proxy *curproxy = bind_conf->frontend;
        int cfgerr = 0;
@@ -4915,7 +4939,7 @@ static int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_con
 
        ssl_set_shctx(ctx);
 #ifndef USE_OPENSSL_WOLFSSL
-       if (!ssl_set_session_id_context(ctx)) {
+       if (!ssl_set_session_id_context(ctx, ckch_inst)) {
                memprintf(err, "%sProxy '%s': unable to set the SSL session context for bind '%s' at [%s:%d].\n",
                          err && *err ? *err : "", bind_conf->frontend->id, bind_conf->arg, bind_conf->file, bind_conf->line);
                cfgerr |= ERR_ALERT | ERR_FATAL;
@@ -5090,7 +5114,7 @@ int ssl_sock_prep_ctx_and_inst(struct bind_conf *bind_conf, struct ssl_bind_conf
 {
        int errcode = 0;
 
-       errcode |= ssl_sock_prepare_ctx(bind_conf, ssl_conf, ctx, err);
+       errcode |= ssl_sock_prepare_ctx(bind_conf, ssl_conf, ctx, ckch_inst, err);
        if (!errcode && ckch_inst)
                ckch_inst_add_cafile_link(ckch_inst, bind_conf, ssl_conf, NULL);