Commit 6d2f848f94 for openssl.org
commit 6d2f848f9463d05d1c00f70b1d024fad9ef03a5d
Author: Daniel Kubec <kubec@openssl.org>
Date: Thu Jan 15 14:18:31 2026 +0000
Added SSL_CTX_get0_alpn_protos() and SSL_get0_alpn_protos()
Fixes #4952
Co-authored-by: Pauli <ppzgs1@gmail.com>
Co-authored-by: Tomáš Mráz <tm@t8m.info>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
MergeDate: Mon Jan 26 15:26:21 2026
(Merged from https://github.com/openssl/openssl/pull/29646)
diff --git a/CHANGES.md b/CHANGES.md
index fbf06a1029..c2193abbd3 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -127,6 +127,10 @@ OpenSSL 4.0
*Igor Ustinov*
+ * Added SSL_CTX_get0_alpn_protos() and SSL_get0_alpn_protos().
+
+ *Daniel Kubec*
+
* Enabled Server verification by default in `s_server` when option
verify_return_error is enabled.
diff --git a/doc/man3/SSL_CTX_set_alpn_select_cb.pod b/doc/man3/SSL_CTX_set_alpn_select_cb.pod
index dd5517df4d..b97ebd1a5a 100644
--- a/doc/man3/SSL_CTX_set_alpn_select_cb.pod
+++ b/doc/man3/SSL_CTX_set_alpn_select_cb.pod
@@ -2,7 +2,8 @@
=head1 NAME
-SSL_CTX_set_alpn_protos, SSL_set_alpn_protos, SSL_CTX_set_alpn_select_cb,
+SSL_CTX_set_alpn_protos, SSL_set_alpn_protos, SSL_CTX_get0_alpn_protos,
+SSL_get0_alpn_protos, SSL_CTX_set_alpn_select_cb,
SSL_CTX_set_next_proto_select_cb, SSL_CTX_set_next_protos_advertised_cb,
SSL_select_next_proto, SSL_get0_alpn_selected, SSL_get0_next_proto_negotiated
- handle application layer protocol negotiation (ALPN)
@@ -47,6 +48,11 @@ SSL_select_next_proto, SSL_get0_alpn_selected, SSL_get0_next_proto_negotiated
void SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data,
unsigned *len);
+ void SSL_CTX_get0_alpn_protos(SSL_CTX *ctx, const unsigned char **protos,
+ unsigned int *protos_len);
+ void SSL_get0_alpn_protos(SSL *ssl, const unsigned char **protos,
+ unsigned int *protos_len);
+
=head1 DESCRIPTION
SSL_CTX_set_alpn_protos() and SSL_set_alpn_protos() are used by the client to
@@ -55,6 +61,12 @@ protocol-list format, described below. The length of B<protos> is specified in
B<protos_len>. Setting B<protos_len> to 0 clears any existing list of ALPN
protocols and no ALPN extension will be sent to the server.
+SSL_CTX_get0_alpn_protos() and SSL_get0_alpn_protos() are used by the client to
+get the list of protocols available to be negotiated. The B<protos> are in
+protocol-list format, described below. Returns a pointer to protocol list in
+B<protos> with length B<protos_len>. It is not NUL-terminated. B<protos> must
+not be freed.
+
SSL_CTX_set_alpn_select_cb() sets the application callback B<cb> used by a
server to select which protocol to use for the incoming connection. When B<cb>
is NULL, ALPN is not used. The B<arg> value is a pointer which is passed to
diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in
index 83d72e6fa8..529c2b71da 100644
--- a/include/openssl/ssl.h.in
+++ b/include/openssl/ssl.h.in
@@ -825,6 +825,10 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
void *arg);
void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data,
unsigned int *len);
+void SSL_CTX_get0_alpn_protos(SSL_CTX *ctx, const unsigned char **protos,
+ unsigned int *protos_len);
+void SSL_get0_alpn_protos(SSL *ssl, const unsigned char **protos,
+ unsigned int *protos_len);
#ifndef OPENSSL_NO_PSK
/*
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 43ca1e46d7..9efda38100 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -3778,6 +3778,47 @@ int SSL_set_alpn_protos(SSL *ssl, const unsigned char *protos,
return 0;
}
+/*
+ * SSL_CTX_set_get0_protos gets the ALPN protocol list on |ctx| to |protos|.
+ */
+void SSL_CTX_get0_alpn_protos(SSL_CTX *ctx, const unsigned char **protos,
+ unsigned int *protos_len)
+{
+ unsigned char *p = NULL;
+ unsigned int len = 0;
+
+ if (ctx != NULL) {
+ p = ctx->ext.alpn;
+ len = (unsigned int)ctx->ext.alpn_len;
+ }
+
+ if (protos != NULL)
+ *protos = p;
+ if (protos_len != NULL)
+ *protos_len = len;
+}
+
+/*
+ * SSL_get0_alpn_protos gets the ALPN protocol list on |ssl| to |protos|.
+ */
+void SSL_get0_alpn_protos(SSL *ssl, const unsigned char **protos,
+ unsigned int *protos_len)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
+ unsigned char *p = NULL;
+ unsigned int len = 0;
+
+ if (sc != NULL) {
+ p = sc->ext.alpn;
+ len = (unsigned int)sc->ext.alpn_len;
+ }
+
+ if (protos != NULL)
+ *protos = p;
+ if (protos_len != NULL)
+ *protos_len = len;
+}
+
/*
* SSL_CTX_set_alpn_select_cb sets a callback function on |ctx| that is
* called during ClientHello processing in order to select an ALPN protocol
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 1fb0477a8c..07a101313c 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -11623,6 +11623,57 @@ end:
return testresult;
}
+static int test_get_alpn(void)
+{
+ SSL_CTX *ctx = NULL;
+ SSL *ssl = NULL;
+ int testresult = 0;
+ unsigned char good[] = { 0x04, 'g', 'o', 'o', 'd' };
+ const unsigned char *expect;
+ unsigned int expect_len;
+
+ /* Create an initial SSL_CTX with no certificate configured */
+ ctx = SSL_CTX_new_ex(libctx, NULL, TLS_server_method());
+ if (!TEST_ptr(ctx))
+ goto end;
+
+ SSL_CTX_get0_alpn_protos(NULL, &expect, &expect_len);
+ if (!TEST_ptr_null(expect))
+ goto end;
+ if (!TEST_int_eq(expect_len, 0))
+ goto end;
+ if (!TEST_false(SSL_CTX_set_alpn_protos(ctx, good, sizeof(good))))
+ goto end;
+
+ SSL_CTX_get0_alpn_protos(ctx, &expect, &expect_len);
+ if (!TEST_mem_eq(expect, expect_len, good, sizeof(good)))
+ goto end;
+
+ ssl = SSL_new(ctx);
+ if (!TEST_ptr(ssl))
+ goto end;
+
+ SSL_get0_alpn_protos(NULL, &expect, &expect_len);
+ if (!TEST_ptr_null(expect))
+ goto end;
+ if (!TEST_int_eq(expect_len, 0))
+ goto end;
+
+ if (!TEST_false(SSL_set_alpn_protos(ssl, good, sizeof(good))))
+ goto end;
+
+ SSL_get0_alpn_protos(ssl, &expect, &expect_len);
+ if (!TEST_mem_eq(expect, expect_len, good, sizeof(good)))
+ goto end;
+
+ testresult = 1;
+
+end:
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ return testresult;
+}
+
#if !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_TLS1_2)
/*
* Complete a connection with legacy EC point format configuration
@@ -14154,6 +14205,7 @@ int setup_tests(void)
#endif
ADD_TEST(test_inherit_verify_param);
ADD_TEST(test_set_alpn);
+ ADD_TEST(test_get_alpn);
#if !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_TLS1_2)
ADD_TEST(test_legacy_ec_point_formats);
#endif
diff --git a/util/libssl.num b/util/libssl.num
index f8ca36f67d..86083e5133 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -54,6 +54,8 @@ SSL_get0_next_proto_negotiated ? 4_0_0 EXIST::FUNCTION:NEXTPROTONEG
SSL_select_next_proto ? 4_0_0 EXIST::FUNCTION:
SSL_CTX_set_alpn_protos ? 4_0_0 EXIST::FUNCTION:
SSL_set_alpn_protos ? 4_0_0 EXIST::FUNCTION:
+SSL_CTX_get0_alpn_protos ? 4_0_0 EXIST::FUNCTION:
+SSL_get0_alpn_protos ? 4_0_0 EXIST::FUNCTION:
SSL_CTX_set_alpn_select_cb ? 4_0_0 EXIST::FUNCTION:
SSL_get0_alpn_selected ? 4_0_0 EXIST::FUNCTION:
SSL_CTX_set_psk_client_callback ? 4_0_0 EXIST::FUNCTION:PSK