This is an automated email from the ASF dual-hosted git repository. markt-asf pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat-native.git
commit 6a6784e8ad7c31b7392d1fae9ce025096af437a5 Author: Mark Thomas <[email protected]> AuthorDate: Mon Sep 21 11:59:53 2026 +0100 Add PSK support for TLS 1.2 Co-authored-by: GitHub Copilot (GPT-5.6 Sol) <[email protected]> --- .../apache/tomcat/jni/PreSharedKeySelector.java | 33 ++++++++ java/org/apache/tomcat/jni/SSLContext.java | 9 ++ native/include/ssl_private.h | 4 + native/src/sslcontext.c | 97 ++++++++++++++++++++++ xdocs/miscellaneous/changelog.xml | 4 + 5 files changed, 147 insertions(+) diff --git a/java/org/apache/tomcat/jni/PreSharedKeySelector.java b/java/org/apache/tomcat/jni/PreSharedKeySelector.java new file mode 100644 index 000000000..0cafc2a90 --- /dev/null +++ b/java/org/apache/tomcat/jni/PreSharedKeySelector.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jni; + +/** + * Is called during a TLSv1.2 handshake and hooked into OpenSSL via {@code SSL_CTX_set_psk_server_callback}. + */ +public interface PreSharedKeySelector { + + /** + * Selects the pre-shared key for the provided identity. + * + * @param ssl the SSL instance + * @param identity the PSK identity provided by the client + * + * @return the pre-shared key, or {@code null} if the identity is not recognized + */ + byte[] select(long ssl, String identity); +} diff --git a/java/org/apache/tomcat/jni/SSLContext.java b/java/org/apache/tomcat/jni/SSLContext.java index e1d2940c5..fa56214fe 100644 --- a/java/org/apache/tomcat/jni/SSLContext.java +++ b/java/org/apache/tomcat/jni/SSLContext.java @@ -471,6 +471,15 @@ public final class SSLContext { */ public static native void setCertVerifyCallback(long ctx, CertificateVerifier verifier); + /** + * Allow to hook {@link PreSharedKeySelector} into the TLSv1.2 handshake processing. This will call + * {@code SSL_CTX_set_psk_server_callback}. + * + * @param ctx Server context to use. + * @param selector the selector to call during handshake, or {@code null} to remove the current selector + */ + public static native void setPskServerCallback(long ctx, PreSharedKeySelector selector); + /** * Set application layer protocol for application layer protocol negotiation extension * diff --git a/native/include/ssl_private.h b/native/include/ssl_private.h index b4e829c3a..8b6575cab 100644 --- a/native/include/ssl_private.h +++ b/native/include/ssl_private.h @@ -195,6 +195,10 @@ struct tcn_ssl_ctxt_t { jobject verifier; jmethodID verifier_method; + /* TLSv1.2 server-side pre-shared key selector */ + jobject psk_selector; + jmethodID psk_selector_method; + /* Holds the alpn protocols, each of them prefixed with the len of the protocol */ unsigned char *alpn_proto_data; unsigned int alpn_proto_len; diff --git a/native/src/sslcontext.c b/native/src/sslcontext.c index 71d158189..c9ce9c73a 100644 --- a/native/src/sslcontext.c +++ b/native/src/sslcontext.c @@ -61,6 +61,14 @@ static apr_status_t ssl_context_cleanup(void *data) } c->verifier_method = NULL; + if (c->psk_selector) { + JNIEnv *e; + tcn_get_java_env(&e); + (*e)->DeleteGlobalRef(e, c->psk_selector); + c->psk_selector = NULL; + } + c->psk_selector_method = NULL; + if (c->alpn_proto_data) { free(c->alpn_proto_data); c->alpn_proto_data = NULL; @@ -1482,6 +1490,95 @@ TCN_IMPLEMENT_CALL(void, SSLContext, setCertVerifyCallback)(TCN_STDARGS, jlong c } } +#ifndef OPENSSL_NO_PSK +static unsigned int SSL_psk_server(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len) +{ + tcn_ssl_ctxt_t *c = SSL_get_app_data2(ssl); + JNIEnv *e; + jstring identity_string = NULL; + jbyteArray key = NULL; + jsize key_len; + unsigned int result = 0; + + if (c == NULL || c->psk_selector == NULL || c->psk_selector_method == NULL || identity == NULL || + tcn_get_java_env(&e) != JNI_OK) { + return 0; + } + + identity_string = (*e)->NewStringUTF(e, identity); + if (identity_string == NULL) { + goto cleanup; + } + + key = (*e)->CallObjectMethod(e, c->psk_selector, c->psk_selector_method, P2J(ssl), identity_string); + if ((*e)->ExceptionCheck(e) || key == NULL) { + goto cleanup; + } + + key_len = (*e)->GetArrayLength(e, key); + if (key_len <= 0 || (unsigned int) key_len > max_psk_len) { + goto cleanup; + } + + (*e)->GetByteArrayRegion(e, key, 0, key_len, (jbyte *)psk); + if ((*e)->ExceptionCheck(e)) { + goto cleanup; + } + result = (unsigned int) key_len; + +cleanup: + if ((*e)->ExceptionCheck(e)) { + (*e)->ExceptionClear(e); + } + if (key != NULL) { + (*e)->DeleteLocalRef(e, key); + } + if (identity_string != NULL) { + (*e)->DeleteLocalRef(e, identity_string); + } + return result; +} +#endif + +TCN_IMPLEMENT_CALL(void, SSLContext, setPskServerCallback)(TCN_STDARGS, jlong ctx, jobject selector) +{ +#ifdef OPENSSL_NO_PSK + UNREFERENCED(o); + UNREFERENCED(ctx); + UNREFERENCED(selector); + tcn_Throw(e, "OpenSSL does not support PSK"); +#else + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); + jobject new_selector = NULL; + jmethodID new_method = NULL; + + UNREFERENCED(o); + TCN_ASSERT(ctx != 0); + + if (selector != NULL) { + jclass selector_class = (*e)->GetObjectClass(e, selector); + new_method = (*e)->GetMethodID(e, selector_class, "select", "(JLjava/lang/String;)[B"); + (*e)->DeleteLocalRef(e, selector_class); + if (new_method == NULL) { + return; + } + + new_selector = (*e)->NewGlobalRef(e, selector); + if (new_selector == NULL) { + return; + } + } + + SSL_CTX_set_psk_server_callback(c->ctx, selector == NULL ? NULL : SSL_psk_server); + + if (c->psk_selector != NULL) { + (*e)->DeleteGlobalRef(e, c->psk_selector); + } + c->psk_selector = new_selector; + c->psk_selector_method = new_method; +#endif +} + TCN_IMPLEMENT_CALL(jboolean, SSLContext, setSessionIdContext)(TCN_STDARGS, jlong ctx, jbyteArray sidCtx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); diff --git a/xdocs/miscellaneous/changelog.xml b/xdocs/miscellaneous/changelog.xml index 6eed1301c..2eeaa964a 100644 --- a/xdocs/miscellaneous/changelog.xml +++ b/xdocs/miscellaneous/changelog.xml @@ -51,6 +51,10 @@ <update> Make Windows 10 / 11 the default target version for Windows builds. (rjung) </update> + <add> + Add support for configuring a server-side TLSv1.2 pre-shared key + callback. (markt) + </add> <fix> Fix a potential crash when negotiating ALPN. (markt) </fix> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
