Files
wolfssl/src/ssl_api_ext.c
T

2423 lines
73 KiB
C

/* ssl_api_ext.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
#if !defined(WOLFSSL_SSL_API_EXT_INCLUDED)
#ifndef WOLFSSL_IGNORE_FILE_WARN
#warning ssl_api_ext.c does not need to be compiled separately from ssl.c
#endif
#else
#ifndef WOLFCRYPT_ONLY
#ifndef NO_TLS
#ifdef HAVE_SNI
/* Set the Server Name Indication extension data on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] type SNI type, e.g. WOLFSSL_SNI_HOST_NAME.
* @param [in] data SNI data.
* @param [in] size Length of SNI data in bytes.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
* @return Negative value on error.
*/
WOLFSSL_ABI
int wolfSSL_UseSNI(WOLFSSL* ssl, byte type, const void* data, word16 size)
{
if (ssl == NULL)
return BAD_FUNC_ARG;
return TLSX_UseSNI(&ssl->extensions, type, data, size, ssl->heap);
}
/* Set the Server Name Indication extension data on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] type SNI type, e.g. WOLFSSL_SNI_HOST_NAME.
* @param [in] data SNI data.
* @param [in] size Length of SNI data in bytes.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ctx is NULL.
* @return Negative value on error.
*/
WOLFSSL_ABI
int wolfSSL_CTX_UseSNI(WOLFSSL_CTX* ctx, byte type, const void* data,
word16 size)
{
if (ctx == NULL)
return BAD_FUNC_ARG;
return TLSX_UseSNI(&ctx->extensions, type, data, size, ctx->heap);
}
#ifndef NO_WOLFSSL_SERVER
/* Set options for the Server Name Indication extension on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] type SNI type.
* @param [in] options Bitmask of SNI options.
*/
void wolfSSL_SNI_SetOptions(WOLFSSL* ssl, byte type, byte options)
{
if ((ssl != NULL) && (ssl->extensions != NULL)) {
TLSX_SNI_SetOptions(ssl->extensions, type, options);
}
}
/* Set options for the Server Name Indication extension on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] type SNI type.
* @param [in] options Bitmask of SNI options.
*/
void wolfSSL_CTX_SNI_SetOptions(WOLFSSL_CTX* ctx, byte type, byte options)
{
if ((ctx != NULL) && (ctx->extensions != NULL)) {
TLSX_SNI_SetOptions(ctx->extensions, type, options);
}
}
/* Get the status of the Server Name Indication extension on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] type SNI type.
* @return SNI status for the type.
*/
byte wolfSSL_SNI_Status(WOLFSSL* ssl, byte type)
{
return TLSX_SNI_Status((ssl != NULL) ? ssl->extensions : NULL, type);
}
/* Get the Server Name Indication request data received from the peer.
*
* @param [in] ssl SSL/TLS object.
* @param [in] type SNI type.
* @param [out] data Pointer to the SNI request data. May be NULL.
* @return Length of the SNI request data in bytes, or 0 when none.
*/
word16 wolfSSL_SNI_GetRequest(WOLFSSL* ssl, byte type, void** data)
{
if (data)
*data = NULL;
if (ssl && ssl->extensions)
return TLSX_SNI_GetRequest(ssl->extensions, type, data, 0);
return 0;
}
/* Get the Server Name Indication data from a raw ClientHello buffer.
*
* @param [in] clientHello ClientHello message buffer.
* @param [in] helloSz Length of the ClientHello in bytes.
* @param [in] type SNI type.
* @param [out] sni Buffer to hold the SNI data.
* @param [in, out] inOutSz In: size of buffer. Out: length of SNI data.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when an argument is NULL or a size is zero.
*/
int wolfSSL_SNI_GetFromBuffer(const byte* clientHello, word32 helloSz,
byte type, byte* sni, word32* inOutSz)
{
if (clientHello && helloSz > 0 && sni && inOutSz && *inOutSz > 0)
return TLSX_SNI_GetFromBuffer(clientHello, helloSz, type, sni, inOutSz);
return BAD_FUNC_ARG;
}
#endif /* !NO_WOLFSSL_SERVER */
#endif /* HAVE_SNI */
#ifdef HAVE_TRUSTED_CA
/* Set the Trusted CA Indication extension on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] type Trusted CA identifier type.
* @param [in] certId Certificate identifier data.
* @param [in] certIdSz Length of certificate identifier in bytes.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL or arguments are inconsistent with
* the type.
*/
int wolfSSL_UseTrustedCA(WOLFSSL* ssl, byte type,
const byte* certId, word32 certIdSz)
{
if (ssl == NULL)
return BAD_FUNC_ARG;
if (type == WOLFSSL_TRUSTED_CA_PRE_AGREED) {
if (certId != NULL || certIdSz != 0)
return BAD_FUNC_ARG;
}
else if (type == WOLFSSL_TRUSTED_CA_X509_NAME) {
if (certId == NULL || certIdSz == 0)
return BAD_FUNC_ARG;
}
#ifndef NO_SHA
else if (type == WOLFSSL_TRUSTED_CA_KEY_SHA1 ||
type == WOLFSSL_TRUSTED_CA_CERT_SHA1) {
if (certId == NULL || certIdSz != WC_SHA_DIGEST_SIZE)
return BAD_FUNC_ARG;
}
#endif
else
return BAD_FUNC_ARG;
return TLSX_UseTrustedCA(&ssl->extensions,
type, certId, certIdSz, ssl->heap);
}
#endif /* HAVE_TRUSTED_CA */
#ifdef HAVE_MAX_FRAGMENT
#ifndef NO_WOLFSSL_CLIENT
/* Set the Maximum Fragment Length extension on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] mfl Maximum fragment length code, e.g. WOLFSSL_MFL_2_9.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
* @return Negative value on error.
*/
int wolfSSL_UseMaxFragment(WOLFSSL* ssl, byte mfl)
{
if (ssl == NULL)
return BAD_FUNC_ARG;
#ifdef WOLFSSL_ALLOW_MAX_FRAGMENT_ADJUST
/* The following is a non-standard way to reconfigure the max packet size
post-handshake for wolfSSL_write/wolfSSL_read */
if (ssl->options.handShakeState == HANDSHAKE_DONE) {
switch (mfl) {
case WOLFSSL_MFL_2_8 : ssl->max_fragment = 256; break;
case WOLFSSL_MFL_2_9 : ssl->max_fragment = 512; break;
case WOLFSSL_MFL_2_10: ssl->max_fragment = 1024; break;
case WOLFSSL_MFL_2_11: ssl->max_fragment = 2048; break;
case WOLFSSL_MFL_2_12: ssl->max_fragment = 4096; break;
case WOLFSSL_MFL_2_13: ssl->max_fragment = 8192; break;
default: ssl->max_fragment = MAX_RECORD_SIZE; break;
}
return WOLFSSL_SUCCESS;
}
#endif /* WOLFSSL_MAX_FRAGMENT_ADJUST */
/* This call sets the max fragment TLS extension, which gets sent to server.
The server_hello response is what sets the `ssl->max_fragment` in
TLSX_MFL_Parse */
return TLSX_UseMaxFragment(&ssl->extensions, mfl, ssl->heap);
}
/* Set the Maximum Fragment Length extension on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] mfl Maximum fragment length code, e.g. WOLFSSL_MFL_2_9.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ctx is NULL.
* @return Negative value on error.
*/
int wolfSSL_CTX_UseMaxFragment(WOLFSSL_CTX* ctx, byte mfl)
{
if (ctx == NULL)
return BAD_FUNC_ARG;
return TLSX_UseMaxFragment(&ctx->extensions, mfl, ctx->heap);
}
#endif /* NO_WOLFSSL_CLIENT */
#endif /* HAVE_MAX_FRAGMENT */
#ifdef HAVE_TRUNCATED_HMAC
#ifndef NO_WOLFSSL_CLIENT
/* Set the Truncated HMAC extension on the object.
*
* @param [in] ssl SSL/TLS object.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
* @return Negative value on error.
*/
int wolfSSL_UseTruncatedHMAC(WOLFSSL* ssl)
{
if (ssl == NULL)
return BAD_FUNC_ARG;
return TLSX_UseTruncatedHMAC(&ssl->extensions, ssl->heap);
}
/* Set the Truncated HMAC extension on the context.
*
* @param [in] ctx SSL/TLS context object.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ctx is NULL.
* @return Negative value on error.
*/
int wolfSSL_CTX_UseTruncatedHMAC(WOLFSSL_CTX* ctx)
{
if (ctx == NULL)
return BAD_FUNC_ARG;
return TLSX_UseTruncatedHMAC(&ctx->extensions, ctx->heap);
}
#endif /* NO_WOLFSSL_CLIENT */
#endif /* HAVE_TRUNCATED_HMAC */
/* Elliptic Curves */
#if defined(HAVE_SUPPORTED_CURVES)
/* Determine whether a named group is a supported curve or FFDHE group.
*
* @param [in] name Named group identifier.
* @return 1 when the named group is valid.
* @return 0 otherwise.
*/
static int isValidCurveGroup(word16 name)
{
switch (name) {
case WOLFSSL_ECC_SECP160K1:
case WOLFSSL_ECC_SECP160R1:
case WOLFSSL_ECC_SECP160R2:
case WOLFSSL_ECC_SECP192K1:
case WOLFSSL_ECC_SECP192R1:
case WOLFSSL_ECC_SECP224K1:
case WOLFSSL_ECC_SECP224R1:
case WOLFSSL_ECC_SECP256K1:
case WOLFSSL_ECC_SECP256R1:
case WOLFSSL_ECC_SECP384R1:
case WOLFSSL_ECC_SECP521R1:
case WOLFSSL_ECC_BRAINPOOLP256R1:
case WOLFSSL_ECC_BRAINPOOLP384R1:
case WOLFSSL_ECC_BRAINPOOLP512R1:
case WOLFSSL_ECC_SM2P256V1:
case WOLFSSL_ECC_X25519:
case WOLFSSL_ECC_X448:
case WOLFSSL_ECC_BRAINPOOLP256R1TLS13:
case WOLFSSL_ECC_BRAINPOOLP384R1TLS13:
case WOLFSSL_ECC_BRAINPOOLP512R1TLS13:
case WOLFSSL_FFDHE_2048:
case WOLFSSL_FFDHE_3072:
case WOLFSSL_FFDHE_4096:
case WOLFSSL_FFDHE_6144:
case WOLFSSL_FFDHE_8192:
#ifdef WOLFSSL_HAVE_MLKEM
#ifndef WOLFSSL_NO_ML_KEM
#ifndef WOLFSSL_TLS_NO_MLKEM_STANDALONE
case WOLFSSL_ML_KEM_512:
case WOLFSSL_ML_KEM_768:
case WOLFSSL_ML_KEM_1024:
#endif /* !WOLFSSL_TLS_NO_MLKEM_STANDALONE */
#ifdef WOLFSSL_PQC_HYBRIDS
case WOLFSSL_SECP384R1MLKEM1024:
case WOLFSSL_X25519MLKEM768:
case WOLFSSL_SECP256R1MLKEM768:
#endif /* WOLFSSL_PQC_HYBRIDS */
#ifdef WOLFSSL_EXTRA_PQC_HYBRIDS
case WOLFSSL_SECP256R1MLKEM512:
case WOLFSSL_SECP384R1MLKEM768:
case WOLFSSL_SECP521R1MLKEM1024:
case WOLFSSL_X25519MLKEM512:
case WOLFSSL_X448MLKEM768:
#endif /* WOLFSSL_EXTRA_PQC_HYBRIDS */
#endif /* !WOLFSSL_NO_ML_KEM */
#ifdef WOLFSSL_MLKEM_KYBER
case WOLFSSL_KYBER_LEVEL1:
case WOLFSSL_KYBER_LEVEL3:
case WOLFSSL_KYBER_LEVEL5:
case WOLFSSL_P256_KYBER_LEVEL1:
case WOLFSSL_P384_KYBER_LEVEL3:
case WOLFSSL_P521_KYBER_LEVEL5:
case WOLFSSL_X25519_KYBER_LEVEL1:
case WOLFSSL_X448_KYBER_LEVEL3:
case WOLFSSL_X25519_KYBER_LEVEL3:
case WOLFSSL_P256_KYBER_LEVEL3:
#endif /* WOLFSSL_MLKEM_KYBER */
#endif
return 1;
default:
return 0;
}
}
/* Set a named group in the Supported Groups extension on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] name Named group identifier.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL or the group is invalid.
* @return WOLFSSL_FAILURE when TLS is not compiled in.
*/
int wolfSSL_UseSupportedCurve(WOLFSSL* ssl, word16 name)
{
if (ssl == NULL || !isValidCurveGroup(name))
return BAD_FUNC_ARG;
ssl->options.userCurves = 1;
#if defined(NO_TLS)
return WOLFSSL_FAILURE;
#else
return TLSX_UseSupportedCurve(&ssl->extensions, name, ssl->heap,
ssl->options.side);
#endif /* NO_TLS */
}
/* Set a named group in the Supported Groups extension on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] name Named group identifier.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ctx is NULL or the group is invalid.
* @return WOLFSSL_FAILURE when TLS is not compiled in.
*/
int wolfSSL_CTX_UseSupportedCurve(WOLFSSL_CTX* ctx, word16 name)
{
if (ctx == NULL || !isValidCurveGroup(name))
return BAD_FUNC_ARG;
ctx->userCurves = 1;
#if defined(NO_TLS)
return WOLFSSL_FAILURE;
#else
return TLSX_UseSupportedCurve(&ctx->extensions, name, ctx->heap,
ctx->method->side);
#endif /* NO_TLS */
}
#if defined(OPENSSL_EXTRA)
/* Validate a list of group identifiers and translate them into named groups.
*
* Group values may be wolfSSL named groups or curve NIDs (when ECC is
* available).
*
* @param [in] groups Array of group identifiers.
* @param [in] count Number of groups in the array.
* @param [out] outGroups Array to hold the named groups. Must have at least
* count entries.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when a group is not recognized.
*/
static int wolfssl_validate_groups(const int* groups, int count, int* outGroups)
{
int i;
int ret = WOLFSSL_SUCCESS;
for (i = 0; i < count; i++) {
if (isValidCurveGroup((word16)groups[i])) {
outGroups[i] = groups[i];
}
#ifdef HAVE_ECC
else {
/* Groups may be populated with curve NIDs. */
int oid = (int)nid2oid(groups[i], oidCurveType);
int name = (int)GetCurveByOID(oid);
if (name == 0) {
WOLFSSL_MSG("Invalid group name");
ret = WOLFSSL_FAILURE;
break;
}
outGroups[i] = name;
}
#else
else {
WOLFSSL_MSG("Invalid group name");
ret = WOLFSSL_FAILURE;
break;
}
#endif
}
return ret;
}
/* Set the list of supported groups on the context.
*
* Group values may be wolfSSL named groups or curve NIDs.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] groups Array of group identifiers.
* @param [in] count Number of groups in the array.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when count is invalid or a group is not recognized.
*/
int wolfSSL_CTX_set1_groups(WOLFSSL_CTX* ctx, int* groups, int count)
{
int _groups[WOLFSSL_MAX_GROUP_COUNT];
int ret = WOLFSSL_SUCCESS;
WOLFSSL_ENTER("wolfSSL_CTX_set1_groups");
if (groups == NULL || count <= 0) {
WOLFSSL_MSG("Groups NULL or count not positive");
ret = WOLFSSL_FAILURE;
}
else if (count > WOLFSSL_MAX_GROUP_COUNT) {
WOLFSSL_MSG("Group count exceeds maximum");
ret = WOLFSSL_FAILURE;
}
else {
/* Translate the input list into named groups, then apply it. */
ret = wolfssl_validate_groups(groups, count, _groups);
if (ret == WOLFSSL_SUCCESS) {
ret = wolfSSL_CTX_set_groups(ctx, _groups, count);
/* Normalize any non-success result to WOLFSSL_FAILURE. */
if (ret != WOLFSSL_SUCCESS) {
ret = WOLFSSL_FAILURE;
}
}
}
return ret;
}
/* Set the list of supported groups on the object.
*
* Group values may be wolfSSL named groups or curve NIDs.
*
* @param [in] ssl SSL/TLS object.
* @param [in] groups Array of group identifiers.
* @param [in] count Number of groups in the array.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when count is invalid or a group is not recognized.
*/
int wolfSSL_set1_groups(WOLFSSL* ssl, int* groups, int count)
{
int _groups[WOLFSSL_MAX_GROUP_COUNT];
int ret = WOLFSSL_SUCCESS;
WOLFSSL_ENTER("wolfSSL_set1_groups");
if (groups == NULL || count <= 0) {
WOLFSSL_MSG("Groups NULL or count not positive");
ret = WOLFSSL_FAILURE;
}
else if (count > WOLFSSL_MAX_GROUP_COUNT) {
WOLFSSL_MSG("Group count exceeds maximum");
ret = WOLFSSL_FAILURE;
}
else {
/* Translate the input list into named groups, then apply it. */
ret = wolfssl_validate_groups(groups, count, _groups);
if (ret == WOLFSSL_SUCCESS) {
ret = wolfSSL_set_groups(ssl, _groups, count);
/* Normalize any non-success result to WOLFSSL_FAILURE. */
if (ret != WOLFSSL_SUCCESS) {
ret = WOLFSSL_FAILURE;
}
}
}
return ret;
}
#endif /* OPENSSL_EXTRA */
#endif /* HAVE_SUPPORTED_CURVES */
/* Application-Layer Protocol Negotiation */
#ifdef HAVE_ALPN
/* Set the Application-Layer Protocol Negotiation extension on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] protocol_name_list Comma-separated list of protocol names.
* @param [in] protocol_name_listSz Length of the list in bytes.
* @param [in] options Bitmask of ALPN options.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when an argument is NULL, the list is too long or
* options are unsupported.
* @return MEMORY_ERROR on allocation failure.
*/
WOLFSSL_ABI
int wolfSSL_UseALPN(WOLFSSL* ssl, char *protocol_name_list,
word32 protocol_name_listSz, byte options)
{
char* list = NULL;
char* ptr = NULL;
char** token = NULL;
word16 len;
int idx = 0;
int ret = WOLFSSL_SUCCESS;
WOLFSSL_ENTER("wolfSSL_UseALPN");
if ((ssl == NULL) || (protocol_name_list == NULL)) {
return BAD_FUNC_ARG;
}
else if (protocol_name_listSz > (WOLFSSL_MAX_ALPN_NUMBER *
WOLFSSL_MAX_ALPN_PROTO_NAME_LEN + WOLFSSL_MAX_ALPN_NUMBER)) {
WOLFSSL_MSG("Invalid arguments, protocol name list too long");
return BAD_FUNC_ARG;
}
else if ((!(options & WOLFSSL_ALPN_CONTINUE_ON_MISMATCH)) &&
(!(options & WOLFSSL_ALPN_FAILED_ON_MISMATCH))) {
WOLFSSL_MSG("Invalid arguments, options not supported");
return BAD_FUNC_ARG;
}
list = (char *)XMALLOC(protocol_name_listSz + 1, ssl->heap,
DYNAMIC_TYPE_ALPN);
token = (char **)XMALLOC(sizeof(char*) * (WOLFSSL_MAX_ALPN_NUMBER + 1),
ssl->heap, DYNAMIC_TYPE_ALPN);
if ((list == NULL) || (token == NULL)) {
WOLFSSL_MSG("Memory failure");
ret = MEMORY_ERROR;
}
if (ret == WOLFSSL_SUCCESS) {
XMEMSET(token, 0, sizeof(char *) * (WOLFSSL_MAX_ALPN_NUMBER+1));
XSTRNCPY(list, protocol_name_list, protocol_name_listSz);
list[protocol_name_listSz] = '\0';
/* Read all protocol names from the list. */
token[idx] = XSTRTOK(list, ",", &ptr);
while ((idx < WOLFSSL_MAX_ALPN_NUMBER) && (token[idx] != NULL)) {
token[++idx] = XSTRTOK(NULL, ",", &ptr);
}
/* Add the protocol name list to the TLS extension in reverse order. */
while ((idx--) > 0) {
len = (word16)XSTRLEN(token[idx]);
ret = TLSX_UseALPN(&ssl->extensions, token[idx], len, options,
ssl->heap);
if (ret != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("TLSX_UseALPN failure");
break;
}
}
}
XFREE(token, ssl->heap, DYNAMIC_TYPE_ALPN);
XFREE(list, ssl->heap, DYNAMIC_TYPE_ALPN);
return ret;
}
/* Get the ALPN protocol negotiated for the object.
*
* @param [in] ssl SSL/TLS object.
* @param [out] protocol_name Negotiated protocol name.
* @param [out] size Length of the protocol name in bytes.
* @return WOLFSSL_SUCCESS on success.
* @return Negative value on error.
*/
int wolfSSL_ALPN_GetProtocol(WOLFSSL* ssl, char **protocol_name, word16 *size)
{
return TLSX_ALPN_GetRequest((ssl != NULL) ? ssl->extensions : NULL,
(void **)protocol_name, size);
}
/* Get the ALPN protocol list offered by the peer as a comma-separated string.
*
* The returned list must be freed with wolfSSL_ALPN_FreePeerProtocol().
*
* @param [in] ssl SSL/TLS object.
* @param [out] list Newly allocated comma-separated protocol list.
* @param [out] listSz Length of the list string.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when an argument is NULL.
* @return BUFFER_ERROR when the peer offered no protocols.
* @return MEMORY_ERROR on allocation failure.
*/
int wolfSSL_ALPN_GetPeerProtocol(WOLFSSL* ssl, char **list, word16 *listSz)
{
int i, len;
char *p;
byte *s;
if (ssl == NULL || list == NULL || listSz == NULL)
return BAD_FUNC_ARG;
if (ssl->alpn_peer_requested == NULL
|| ssl->alpn_peer_requested_length == 0)
return BUFFER_ERROR;
/* ssl->alpn_peer_requested are the original bytes sent in a ClientHello,
* formatted as (len-byte chars+)+. To turn n protocols into a
* comma-separated C string, one needs (n-1) commas and a final 0 byte
* which has the same length as the original.
* The returned length is the strlen() of the C string, so -1 of that. */
*listSz = ssl->alpn_peer_requested_length-1;
*list = p = (char *)XMALLOC(ssl->alpn_peer_requested_length, ssl->heap,
DYNAMIC_TYPE_TLSX);
if (p == NULL)
return MEMORY_ERROR;
for (i = 0, s = ssl->alpn_peer_requested;
i < ssl->alpn_peer_requested_length;
p += len, i += len)
{
if (i)
*p++ = ',';
len = s[i++];
/* guard against bad length bytes. */
if (i + len > ssl->alpn_peer_requested_length) {
XFREE(*list, ssl->heap, DYNAMIC_TYPE_TLSX);
*list = NULL;
return WOLFSSL_FAILURE;
}
XMEMCPY(p, s + i, (size_t)len);
}
*p = 0;
return WOLFSSL_SUCCESS;
}
/* Free a peer protocol list returned by wolfSSL_ALPN_GetPeerProtocol().
*
* @param [in] ssl SSL/TLS object.
* @param [in, out] list Protocol list to free; set to NULL on return.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
*/
int wolfSSL_ALPN_FreePeerProtocol(WOLFSSL* ssl, char **list)
{
if (ssl == NULL) {
return BAD_FUNC_ARG;
}
XFREE(*list, ssl->heap, DYNAMIC_TYPE_TLSX);
*list = NULL;
return WOLFSSL_SUCCESS;
}
#endif /* HAVE_ALPN */
/* Secure Renegotiation */
#ifdef HAVE_SERVER_RENEGOTIATION_INFO
/* Enable the Secure Renegotiation extension on the object.
*
* Use of secure renegotiation is discouraged.
*
* @param [in] ssl SSL/TLS object.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
* @return Negative value on error.
*/
int wolfSSL_UseSecureRenegotiation(WOLFSSL* ssl)
{
int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG);
#if defined(NO_TLS)
(void)ssl;
#else
if (ssl != NULL) {
ret = TLSX_UseSecureRenegotiation(&ssl->extensions, ssl->heap);
}
else {
ret = BAD_FUNC_ARG;
}
if (ret == WOLFSSL_SUCCESS) {
TLSX* extension = TLSX_Find(ssl->extensions, TLSX_RENEGOTIATION_INFO);
if (extension != NULL) {
ssl->secure_renegotiation = (SecureRenegotiation*)extension->data;
}
}
#endif /* !NO_TLS */
return ret;
}
/* Enable the Secure Renegotiation extension on the context.
*
* Use of secure renegotiation is discouraged.
*
* @param [in] ctx SSL/TLS context object.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ctx is NULL.
*/
int wolfSSL_CTX_UseSecureRenegotiation(WOLFSSL_CTX* ctx)
{
if (ctx == NULL)
return BAD_FUNC_ARG;
ctx->useSecureReneg = 1;
return WOLFSSL_SUCCESS;
}
#ifdef HAVE_SECURE_RENEGOTIATION
/* Perform a secure renegotiation handshake on the object.
*
* User forced; use of secure renegotiation is discouraged.
*
* @param [in] ssl SSL/TLS object.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
* @return SECURE_RENEGOTIATION_E when renegotiation is not allowed.
* @return WOLFSSL_FATAL_ERROR on error.
*/
static int _Rehandshake(WOLFSSL* ssl)
{
int ret;
if (ssl == NULL)
return BAD_FUNC_ARG;
if (IsAtLeastTLSv1_3(ssl->version)) {
WOLFSSL_MSG("Secure Renegotiation not supported in TLS 1.3");
return SECURE_RENEGOTIATION_E;
}
if (ssl->secure_renegotiation == NULL) {
WOLFSSL_MSG("Secure Renegotiation not forced on by user");
return SECURE_RENEGOTIATION_E;
}
if (ssl->secure_renegotiation->enabled == 0) {
WOLFSSL_MSG("Secure Renegotiation not enabled at extension level");
return SECURE_RENEGOTIATION_E;
}
#ifdef WOLFSSL_DTLS
if (ssl->options.dtls && ssl->keys.dtls_epoch == 0xFFFF) {
WOLFSSL_MSG("Secure Renegotiation not allowed. Epoch would wrap");
return SECURE_RENEGOTIATION_E;
}
#endif
/* If the client started the renegotiation, the server will already
* have processed the client's hello. */
if (ssl->options.side != WOLFSSL_SERVER_END ||
ssl->options.acceptState != ACCEPT_FIRST_REPLY_DONE) {
if (ssl->options.handShakeState != HANDSHAKE_DONE) {
if (!ssl->options.handShakeDone) {
WOLFSSL_MSG("Can't renegotiate until initial "
"handshake complete");
return SECURE_RENEGOTIATION_E;
}
else {
WOLFSSL_MSG("Renegotiation already started. "
"Moving it forward.");
ret = wolfSSL_negotiate(ssl);
if (ret == WOLFSSL_SUCCESS)
ssl->secure_rene_count++;
return ret;
}
}
/* reset handshake states */
ssl->options.sendVerify = 0;
ssl->options.serverState = NULL_STATE;
ssl->options.clientState = NULL_STATE;
ssl->options.connectState = CONNECT_BEGIN;
ssl->options.acceptState = ACCEPT_BEGIN_RENEG;
ssl->options.handShakeState = NULL_STATE;
ssl->options.processReply = 0; /* TODO, move states in internal.h */
XMEMSET(&ssl->msgsReceived, 0, sizeof(ssl->msgsReceived));
ssl->secure_renegotiation->cache_status = SCR_CACHE_NEEDED;
#if !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_TLS12)
if (ssl->options.side == WOLFSSL_SERVER_END) {
ret = SendHelloRequest(ssl);
if (ret != 0) {
ssl->error = ret;
return WOLFSSL_FATAL_ERROR;
}
}
#endif /* !NO_WOLFSSL_SERVER && !WOLFSSL_NO_TLS12 */
ret = InitHandshakeHashes(ssl);
if (ret != 0) {
ssl->error = ret;
return WOLFSSL_FATAL_ERROR;
}
}
ret = wolfSSL_negotiate(ssl);
if (ret == WOLFSSL_SUCCESS)
ssl->secure_rene_count++;
return ret;
}
/* Perform a secure renegotiation handshake on the object.
*
* User forced; use of secure renegotiation is discouraged.
*
* @param [in] ssl SSL/TLS object.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ssl is NULL.
* @return Negative value on error.
*/
int wolfSSL_Rehandshake(WOLFSSL* ssl)
{
int ret;
WOLFSSL_ENTER("wolfSSL_Rehandshake");
if (ssl == NULL)
return WOLFSSL_FAILURE;
#ifdef HAVE_SESSION_TICKET
ret = WOLFSSL_SUCCESS;
#endif
if (ssl->options.side == WOLFSSL_SERVER_END) {
/* Reset option to send certificate verify. */
ssl->options.sendVerify = 0;
/* Reset resuming flag to do full secure handshake. */
ssl->options.resuming = 0;
}
else {
/* Reset resuming flag to do full secure handshake. */
ssl->options.resuming = 0;
#if defined(HAVE_SESSION_TICKET) && !defined(NO_WOLFSSL_CLIENT)
/* Clearing the ticket. */
ret = wolfSSL_UseSessionTicket(ssl);
#endif
}
/* CLIENT/SERVER: Reset peer authentication for full secure handshake. */
ssl->options.peerAuthGood = 0;
#ifdef HAVE_SESSION_TICKET
if (ret == WOLFSSL_SUCCESS)
#endif
ret = _Rehandshake(ssl);
return ret;
}
#ifndef NO_WOLFSSL_CLIENT
/* Perform a secure resumption handshake on the object.
*
* Client side only. User forced; use of secure renegotiation is discouraged.
*
* @param [in] ssl SSL/TLS object.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
* @return WOLFSSL_FATAL_ERROR when called on a server.
*/
int wolfSSL_SecureResume(WOLFSSL* ssl)
{
WOLFSSL_ENTER("wolfSSL_SecureResume");
if (ssl == NULL)
return BAD_FUNC_ARG;
if (ssl->options.side == WOLFSSL_SERVER_END) {
ssl->error = SIDE_ERROR;
return WOLFSSL_FATAL_ERROR;
}
return _Rehandshake(ssl);
}
#endif /* NO_WOLFSSL_CLIENT */
#endif /* HAVE_SECURE_RENEGOTIATION */
/* Get whether secure renegotiation is enabled for the object.
*
* @param [in] ssl SSL/TLS object.
* @return 1 when secure renegotiation is enabled.
* @return 0 when ssl is NULL or it is not enabled.
*/
long wolfSSL_SSL_get_secure_renegotiation_support(WOLFSSL* ssl)
{
WOLFSSL_ENTER("wolfSSL_SSL_get_secure_renegotiation_support");
return (ssl != NULL) && (ssl->secure_renegotiation != NULL) &&
ssl->secure_renegotiation->enabled;
}
#endif /* HAVE_SECURE_RENEGOTIATION_INFO */
#if !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \
defined(WOLFSSL_HARDEN_TLS) && !defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK)
/* Get whether the secure renegotiation check is enabled for the object.
*
* @param [in] ssl SSL/TLS object.
* @return Non-zero when the check is enabled, 0 otherwise.
* @return BAD_FUNC_ARG when ssl is NULL.
*/
WOLFSSL_API int wolfSSL_get_scr_check_enabled(const WOLFSSL* ssl)
{
WOLFSSL_ENTER("wolfSSL_get_scr_check_enabled");
if (ssl == NULL)
return BAD_FUNC_ARG;
return ssl->scr_check_enabled;
}
/* Set whether the secure renegotiation check is enabled for the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] enabled Non-zero to enable the check, 0 to disable it.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
*/
WOLFSSL_API int wolfSSL_set_scr_check_enabled(WOLFSSL* ssl, byte enabled)
{
WOLFSSL_ENTER("wolfSSL_set_scr_check_enabled");
if (ssl == NULL)
return BAD_FUNC_ARG;
ssl->scr_check_enabled = !!enabled;
return WOLFSSL_SUCCESS;
}
#endif
#if defined(HAVE_SESSION_TICKET)
/* Session Ticket */
#if !defined(NO_WOLFSSL_SERVER)
/* Disable use of session tickets with TLS 1.2 on the context.
*
* @param [in] ctx SSL/TLS context object.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ctx is NULL.
*/
int wolfSSL_CTX_NoTicketTLSv12(WOLFSSL_CTX* ctx)
{
if (ctx == NULL)
return BAD_FUNC_ARG;
ctx->noTicketTls12 = 1;
return WOLFSSL_SUCCESS;
}
/* Disable use of session tickets with TLS 1.2 on the object.
*
* @param [in] ssl SSL/TLS object.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
*/
int wolfSSL_NoTicketTLSv12(WOLFSSL* ssl)
{
if (ssl == NULL)
return BAD_FUNC_ARG;
ssl->options.noTicketTls12 = 1;
return WOLFSSL_SUCCESS;
}
/* Set the session ticket encryption callback on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] cb Session ticket encryption callback.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ctx is NULL.
*/
int wolfSSL_CTX_set_TicketEncCb(WOLFSSL_CTX* ctx, SessionTicketEncCb cb)
{
if (ctx == NULL)
return BAD_FUNC_ARG;
ctx->ticketEncCb = cb;
return WOLFSSL_SUCCESS;
}
/* Set the session ticket lifetime hint, in seconds, on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] hint Lifetime hint in seconds. No more than 604800 (7 days).
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ctx is NULL or hint is out of range.
*/
int wolfSSL_CTX_set_TicketHint(WOLFSSL_CTX* ctx, int hint)
{
if (ctx == NULL)
return BAD_FUNC_ARG;
/* RFC8446 Section 4.6.1: Servers MUST NOT use any value greater than
* 604800 seconds (7 days). */
if (hint < 0 || hint > 604800)
return BAD_FUNC_ARG;
ctx->ticketHint = hint;
return WOLFSSL_SUCCESS;
}
/* Set the user context passed to the session ticket encryption callback.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] userCtx User context for the ticket encryption callback.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ctx is NULL.
*/
int wolfSSL_CTX_set_TicketEncCtx(WOLFSSL_CTX* ctx, void* userCtx)
{
if (ctx == NULL)
return BAD_FUNC_ARG;
ctx->ticketEncCtx = userCtx;
return WOLFSSL_SUCCESS;
}
/* Get the user context passed to the session ticket encryption callback.
*
* @param [in] ctx SSL/TLS context object.
* @return User context on success.
* @return NULL when ctx is NULL.
*/
void* wolfSSL_CTX_get_TicketEncCtx(WOLFSSL_CTX* ctx)
{
if (ctx == NULL)
return NULL;
return ctx->ticketEncCtx;
}
#ifdef WOLFSSL_TLS13
/* Set the maximum number of TLS 1.3 session tickets to send.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] mxTickets Maximum number of tickets to send.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ctx is NULL.
*/
int wolfSSL_CTX_set_num_tickets(WOLFSSL_CTX* ctx, size_t mxTickets)
{
if (ctx == NULL)
return WOLFSSL_FAILURE;
ctx->maxTicketTls13 = (unsigned int)mxTickets;
return WOLFSSL_SUCCESS;
}
/* Get the maximum number of TLS 1.3 session tickets to send.
*
* @param [in] ctx SSL/TLS context object.
* @return Maximum number of tickets to send, or 0 when ctx is NULL.
*/
size_t wolfSSL_CTX_get_num_tickets(WOLFSSL_CTX* ctx)
{
if (ctx == NULL)
return 0;
return (size_t)ctx->maxTicketTls13;
}
#endif /* WOLFSSL_TLS13 */
#endif /* !NO_WOLFSSL_SERVER */
#if !defined(NO_WOLFSSL_CLIENT)
/* Enable use of the session ticket extension on the object.
*
* @param [in] ssl SSL/TLS object.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
* @return Negative value on error.
*/
int wolfSSL_UseSessionTicket(WOLFSSL* ssl)
{
if (ssl == NULL)
return BAD_FUNC_ARG;
return TLSX_UseSessionTicket(&ssl->extensions, NULL, ssl->heap);
}
/* Enable use of the session ticket extension on the context.
*
* @param [in] ctx SSL/TLS context object.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ctx is NULL.
* @return Negative value on error.
*/
int wolfSSL_CTX_UseSessionTicket(WOLFSSL_CTX* ctx)
{
if (ctx == NULL)
return BAD_FUNC_ARG;
return TLSX_UseSessionTicket(&ctx->extensions, NULL, ctx->heap);
}
/* Get the session ticket stored on the object.
*
* When buf is NULL and *bufSz is 0, the length required is returned in bufSz.
*
* @param [in] ssl SSL/TLS object.
* @param [out] buf Buffer to hold the ticket. May be NULL for length.
* @param [in, out] bufSz In: size of buffer. Out: length of ticket.
* @return WOLFSSL_SUCCESS on success.
* @return LENGTH_ONLY_E when buf is NULL and bufSz has been set.
* @return BAD_FUNC_ARG when ssl or bufSz is NULL.
*/
int wolfSSL_get_SessionTicket(WOLFSSL* ssl, byte* buf, word32* bufSz)
{
if (ssl == NULL || bufSz == NULL)
return BAD_FUNC_ARG;
if (*bufSz == 0 && buf == NULL) {
*bufSz = ssl->session->ticketLen;
return LENGTH_ONLY_E;
}
if (buf == NULL)
return BAD_FUNC_ARG;
if (ssl->session->ticketLen <= *bufSz) {
XMEMCPY(buf, ssl->session->ticket, ssl->session->ticketLen);
*bufSz = ssl->session->ticketLen;
}
else
*bufSz = 0;
return WOLFSSL_SUCCESS;
}
/* Set the session ticket to use on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] buf Ticket data, may be NULL when bufSz is 0.
* @param [in] bufSz Length of ticket data in bytes.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL or buf is NULL with bufSz > 0.
* @return MEMORY_ERROR on allocation failure.
*/
int wolfSSL_set_SessionTicket(WOLFSSL* ssl, const byte* buf,
word32 bufSz)
{
if (ssl == NULL || (buf == NULL && bufSz > 0))
return BAD_FUNC_ARG;
if (bufSz > 0) {
/* Ticket will fit into static ticket */
if (bufSz <= SESSION_TICKET_LEN) {
if (ssl->session->ticketLenAlloc > 0) {
XFREE(ssl->session->ticket, ssl->session->heap,
DYNAMIC_TYPE_SESSION_TICK);
ssl->session->ticketLenAlloc = 0;
ssl->session->ticket = ssl->session->staticTicket;
}
}
else { /* Ticket requires dynamic ticket storage */
/* is dyn buffer big enough */
if (ssl->session->ticketLen < bufSz) {
if (ssl->session->ticketLenAlloc > 0) {
XFREE(ssl->session->ticket, ssl->session->heap,
DYNAMIC_TYPE_SESSION_TICK);
}
ssl->session->ticket = (byte*)XMALLOC(bufSz, ssl->session->heap,
DYNAMIC_TYPE_SESSION_TICK);
if(ssl->session->ticket == NULL) {
ssl->session->ticket = ssl->session->staticTicket;
ssl->session->ticketLenAlloc = 0;
return MEMORY_ERROR;
}
ssl->session->ticketLenAlloc = (word16)bufSz;
}
}
XMEMCPY(ssl->session->ticket, buf, bufSz);
}
ssl->session->ticketLen = (word16)bufSz;
return WOLFSSL_SUCCESS;
}
/* Set the session ticket callback and user context on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] cb Session ticket callback.
* @param [in] ctx User context passed to the callback.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
*/
int wolfSSL_set_SessionTicket_cb(WOLFSSL* ssl,
CallbackSessionTicket cb, void* ctx)
{
if (ssl == NULL)
return BAD_FUNC_ARG;
ssl->session_ticket_cb = cb;
ssl->session_ticket_ctx = ctx;
return WOLFSSL_SUCCESS;
}
#endif /* !NO_WOLFSSL_CLIENT */
#endif /* HAVE_SESSION_TICKET */
#ifdef HAVE_EXTENDED_MASTER
#ifndef NO_WOLFSSL_CLIENT
/* Disable the Extended Master Secret extension on the context.
*
* @param [in] ctx SSL/TLS context object.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ctx is NULL.
*/
int wolfSSL_CTX_DisableExtendedMasterSecret(WOLFSSL_CTX* ctx)
{
if (ctx == NULL)
return BAD_FUNC_ARG;
ctx->haveEMS = 0;
return WOLFSSL_SUCCESS;
}
/* Disable the Extended Master Secret extension on the object.
*
* @param [in] ssl SSL/TLS object.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
*/
int wolfSSL_DisableExtendedMasterSecret(WOLFSSL* ssl)
{
if (ssl == NULL)
return BAD_FUNC_ARG;
ssl->options.haveEMS = 0;
return WOLFSSL_SUCCESS;
}
#endif
#endif
#endif /* !NO_TLS */
/* ---- OpenSSL-compatibility TLS extension APIs (moved from ssl.c) ---- */
#ifdef OPENSSL_EXTRA
#ifdef HAVE_PK_CALLBACKS
/* Set the debug argument passed to the logging callback on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] arg Debug argument.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ssl is NULL.
*/
long wolfSSL_set_tlsext_debug_arg(WOLFSSL* ssl, void *arg)
{
if (ssl == NULL) {
return WOLFSSL_FAILURE;
}
ssl->loggingCtx = arg;
return WOLFSSL_SUCCESS;
}
#endif /* HAVE_PK_CALLBACKS */
#ifndef NO_WOLFSSL_STUB
/* Get the certificate status request extensions on the object.
*
* Not implemented - stub for OpenSSL compatibility.
*
* @param [in] s SSL/TLS object.
* @param [in] arg Ignored.
* @return WOLFSSL_FAILURE always.
*/
long wolfSSL_get_tlsext_status_exts(WOLFSSL *s, void *arg)
{
(void)s;
(void)arg;
WOLFSSL_STUB("wolfSSL_get_tlsext_status_exts");
return WOLFSSL_FAILURE;
}
#endif
/* Set the certificate status request extensions on the object.
*
* Not implemented - stub for OpenSSL compatibility.
*
* @param [in] s SSL/TLS object.
* @param [in] arg Ignored.
* @return WOLFSSL_FAILURE always.
*/
#ifndef NO_WOLFSSL_STUB
long wolfSSL_set_tlsext_status_exts(WOLFSSL *s, void *arg)
{
(void)s;
(void)arg;
WOLFSSL_STUB("wolfSSL_set_tlsext_status_exts");
return WOLFSSL_FAILURE;
}
#endif
/* Get the certificate status request responder ids on the object.
*
* Not implemented - stub for OpenSSL compatibility.
*
* @param [in] s SSL/TLS object.
* @param [in] arg Ignored.
* @return WOLFSSL_FAILURE always.
*/
#ifndef NO_WOLFSSL_STUB
long wolfSSL_get_tlsext_status_ids(WOLFSSL *s, void *arg)
{
(void)s;
(void)arg;
WOLFSSL_STUB("wolfSSL_get_tlsext_status_ids");
return WOLFSSL_FAILURE;
}
#endif
/* Set the certificate status request responder ids on the object.
*
* Not implemented - stub for OpenSSL compatibility.
*
* @param [in] s SSL/TLS object.
* @param [in] arg Ignored.
* @return WOLFSSL_FAILURE always.
*/
#ifndef NO_WOLFSSL_STUB
long wolfSSL_set_tlsext_status_ids(WOLFSSL *s, void *arg)
{
(void)s;
(void)arg;
WOLFSSL_STUB("wolfSSL_set_tlsext_status_ids");
return WOLFSSL_FAILURE;
}
#endif
#ifdef HAVE_MAX_FRAGMENT
#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS)
/* Set the Maximum Fragment Length extension on the context.
*
* @param [in] c SSL/TLS context object.
* @param [in] mode Maximum fragment length mode, e.g. WOLFSSL_MFL_2_9.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when c is NULL or mode is out of range.
*/
int wolfSSL_CTX_set_tlsext_max_fragment_length(WOLFSSL_CTX *c,
unsigned char mode)
{
if (c == NULL || (mode < WOLFSSL_MFL_2_9 || mode > WOLFSSL_MFL_2_12 ))
return BAD_FUNC_ARG;
return wolfSSL_CTX_UseMaxFragment(c, mode);
}
/* Set the Maximum Fragment Length extension on the object.
*
* @param [in] s SSL/TLS object.
* @param [in] mode Maximum fragment length mode, e.g. WOLFSSL_MFL_2_9.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when s is NULL or mode is out of range.
*/
int wolfSSL_set_tlsext_max_fragment_length(WOLFSSL *s, unsigned char mode)
{
if (s == NULL || (mode < WOLFSSL_MFL_2_9 || mode > WOLFSSL_MFL_2_12 ))
return BAD_FUNC_ARG;
return wolfSSL_UseMaxFragment(s, mode);
}
#endif /* !NO_WOLFSSL_CLIENT && !NO_TLS */
#endif /* HAVE_MAX_FRAGMENT */
/* Set the signature algorithms list on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] list Colon-separated list of <public key>+<digest> algorithms.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ctx or list is NULL or on error.
*/
int wolfSSL_CTX_set1_sigalgs_list(WOLFSSL_CTX* ctx, const char* list)
{
WOLFSSL_MSG("wolfSSL_CTX_set1_sigalg_list");
if (ctx == NULL || list == NULL) {
WOLFSSL_MSG("Bad function arguments");
return WOLFSSL_FAILURE;
}
if (AllocateCtxSuites(ctx) != 0)
return WOLFSSL_FAILURE;
return SetSuitesHashSigAlgo(ctx->suites, list);
}
/* Set the signature algorithms list on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] list Colon-separated list of <public key>+<digest> algorithms.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ssl or list is NULL or on error.
*/
int wolfSSL_set1_sigalgs_list(WOLFSSL* ssl, const char* list)
{
WOLFSSL_MSG("wolfSSL_set1_sigalg_list");
if (ssl == NULL || list == NULL) {
WOLFSSL_MSG("Bad function arguments");
return WOLFSSL_FAILURE;
}
if (AllocateSuites(ssl) != 0)
return WOLFSSL_FAILURE;
return SetSuitesHashSigAlgo(ssl->suites, list);
}
#ifdef HAVE_ECC
#if defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES)
/* Set the supported groups list, by name, on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] list Colon-separated list of group names.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ctx or list is NULL or on error.
*/
int wolfSSL_CTX_set1_groups_list(WOLFSSL_CTX *ctx, const char *list)
{
if (!ctx || !list) {
return WOLFSSL_FAILURE;
}
return set_curves_list(NULL, ctx, list, 0);
}
/* Set the supported groups list, by name, on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] list Colon-separated list of group names.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ssl or list is NULL or on error.
*/
int wolfSSL_set1_groups_list(WOLFSSL *ssl, const char *list)
{
if (!ssl || !list) {
return WOLFSSL_FAILURE;
}
return set_curves_list(ssl, NULL, list, 0);
}
#endif /* WOLFSSL_TLS13 */
#endif /* HAVE_ECC */
#endif /* OPENSSL_EXTRA */
#if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)
#ifdef HAVE_SNI
/* Set the SNI host name extension on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] host_name Host name string.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when ssl is NULL.
* @return Negative value on error.
*/
int wolfSSL_set_tlsext_host_name(WOLFSSL* ssl, const char* host_name)
{
int ret;
WOLFSSL_ENTER("wolfSSL_set_tlsext_host_name");
ret = wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, host_name,
(word16)XSTRLEN(host_name));
WOLFSSL_LEAVE("wolfSSL_set_tlsext_host_name", ret);
return ret;
}
#ifndef NO_WOLFSSL_SERVER
/* Get the SNI host name requested for the object.
*
* May be called by a server to get the accepted name or by a client to get
* the requested name.
*
* @param [in] ssl SSL/TLS object.
* @param [in] type SNI type.
* @return Requested server name on success.
* @return NULL when ssl is NULL or no name is set.
*/
const char * wolfSSL_get_servername(WOLFSSL* ssl, byte type)
{
void * serverName = NULL;
if (ssl == NULL)
return NULL;
TLSX_SNI_GetRequest(ssl->extensions, type, &serverName,
!wolfSSL_is_server(ssl));
return (const char *)serverName;
}
#endif
#endif /* HAVE_SNI */
#ifdef HAVE_SNI
/* Set the SNI receive callback on the context.
*
* Compatibility function; consider using wolfSSL_CTX_set_servername_callback().
*
* @param [in] ctx SSL/TLS context object.
* @param [in] cb SNI receive callback.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ctx is NULL.
*/
int wolfSSL_CTX_set_tlsext_servername_callback(WOLFSSL_CTX* ctx,
CallbackSniRecv cb)
{
WOLFSSL_ENTER("wolfSSL_CTX_set_tlsext_servername_callback");
if (ctx) {
ctx->sniRecvCb = cb;
return WOLFSSL_SUCCESS;
}
return WOLFSSL_FAILURE;
}
#endif /* HAVE_SNI */
#endif /* OPENSSL_ALL || OPENSSL_EXTRA */
#ifdef HAVE_SNI
/* Set the SNI receive callback on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] cb SNI receive callback.
*/
void wolfSSL_CTX_set_servername_callback(WOLFSSL_CTX* ctx, CallbackSniRecv cb)
{
WOLFSSL_ENTER("wolfSSL_CTX_set_servername_callback");
if (ctx != NULL) {
ctx->sniRecvCb = cb;
}
}
/* Set the user argument passed to the SNI receive callback on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] arg User argument for the SNI receive callback.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ctx is NULL.
*/
int wolfSSL_CTX_set_servername_arg(WOLFSSL_CTX* ctx, void* arg)
{
WOLFSSL_ENTER("wolfSSL_CTX_set_servername_arg");
if (ctx) {
ctx->sniRecvCbArg = arg;
return WOLFSSL_SUCCESS;
}
return WOLFSSL_FAILURE;
}
#endif /* HAVE_SNI */
#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) \
|| defined(OPENSSL_EXTRA) || defined(HAVE_LIGHTY)
#if defined(HAVE_SESSION_TICKET) && !defined(NO_WOLFSSL_SERVER)
/* Expected return values from implementations of OpenSSL ticket key callback.
*/
#define TICKET_KEY_CB_RET_FAILURE (-1)
#define TICKET_KEY_CB_RET_NOT_FOUND 0
#define TICKET_KEY_CB_RET_OK 1
#define TICKET_KEY_CB_RET_RENEW 2
/* Encrypt the ticket data in place and compute the HMAC over it.
*
* @param [in] evpCtx Cipher context initialized by the callback.
* @param [in] hmacCtx HMAC context initialized by the callback.
* @param [in, out] encTicket Ticket data, encrypted in place.
* @param [in] encTicketLen Length of the plaintext ticket data in bytes.
* @param [in] encSz Capacity of the ticket buffer in bytes.
* @param [out] mac HMAC of the encrypted data.
* @param [out] outSz Length of the encrypted data in bytes.
* @return 1 on success.
* @return 0 on error.
*/
static int wolfssl_ticket_key_enc(WOLFSSL_EVP_CIPHER_CTX* evpCtx,
WOLFSSL_HMAC_CTX* hmacCtx, unsigned char* encTicket, int encTicketLen,
int encSz, unsigned char* mac, int* outSz)
{
int ret = 1;
int len = 0;
int totalSz = 0;
unsigned int mdSz = 0;
/* Encrypt in place. */
if (!wolfSSL_EVP_CipherUpdate(evpCtx, encTicket, &len, encTicket,
encTicketLen)) {
ret = 0;
}
if (ret == 1) {
totalSz = len;
/* Encrypted data must fit in the output buffer. */
if (totalSz > encSz) {
ret = 0;
}
}
if ((ret == 1) &&
(!wolfSSL_EVP_EncryptFinal(evpCtx, &encTicket[len], &len))) {
ret = 0;
}
if (ret == 1) {
/* Total length of encrypted data. */
totalSz += len;
if (totalSz > encSz) {
ret = 0;
}
}
/* HMAC the encrypted data into the parameter 'mac'. */
if ((ret == 1) && (!wolfSSL_HMAC_Update(hmacCtx, encTicket, totalSz))) {
ret = 0;
}
if ((ret == 1) && (!wolfSSL_HMAC_Final(hmacCtx, mac, &mdSz))) {
ret = 0;
}
if (ret == 1) {
*outSz = totalSz;
}
return ret;
}
/* Verify the ticket HMAC then decrypt the ticket data in place.
*
* @param [in] evpCtx Cipher context initialized by the callback.
* @param [in] hmacCtx HMAC context initialized by the callback.
* @param [in, out] encTicket Ticket data, decrypted in place.
* @param [in] encTicketLen Length of the encrypted ticket data in bytes.
* @param [in] mac Expected HMAC of the encrypted data.
* @param [out] outSz Length of the decrypted data in bytes.
* @return 1 on success.
* @return 0 on error or when the HMAC does not match.
*/
static int wolfssl_ticket_key_dec(WOLFSSL_EVP_CIPHER_CTX* evpCtx,
WOLFSSL_HMAC_CTX* hmacCtx, unsigned char* encTicket, int encTicketLen,
const unsigned char* mac, int* outSz)
{
int ret = 1;
int len = 0;
int totalSz = 0;
unsigned int mdSz = 0;
byte digest[WC_MAX_DIGEST_SIZE];
/* HMAC the encrypted data and compare it to the passed in data. */
if (!wolfSSL_HMAC_Update(hmacCtx, encTicket, encTicketLen)) {
ret = 0;
}
if ((ret == 1) && (!wolfSSL_HMAC_Final(hmacCtx, digest, &mdSz))) {
ret = 0;
}
if ((ret == 1) && (ConstantCompare(mac, digest, (int)mdSz) != 0)) {
ret = 0;
}
/* Decrypt the ticket data in place. */
if ((ret == 1) &&
(!wolfSSL_EVP_CipherUpdate(evpCtx, encTicket, &len, encTicket,
encTicketLen))) {
ret = 0;
}
if (ret == 1) {
totalSz = len;
/* Decrypted data must fit in the buffer. */
if (totalSz > encTicketLen) {
ret = 0;
}
}
if ((ret == 1) &&
(!wolfSSL_EVP_DecryptFinal(evpCtx, &encTicket[len], &len))) {
ret = 0;
}
if (ret == 1) {
/* Total length of decrypted data. */
totalSz += len;
if (totalSz > encTicketLen) {
ret = 0;
}
}
if (ret == 1) {
*outSz = totalSz;
}
return ret;
}
/* Encrypt or decrypt a session ticket using the OpenSSL ticket key callback.
*
* Wraps the application's OpenSSL-style callback that initializes the cipher
* and HMAC.
*
* @param [in] ssl SSL/TLS object.
* @param [in] keyName Key name identifying the key to use.
* @param [in] iv IV to use.
* @param [in, out] mac MAC of the encrypted data.
* @param [in] enc 1 to encrypt the ticket, 0 to decrypt.
* @param [in, out] encTicket Ticket data, encrypted/decrypted in place.
* @param [in] encTicketLen Length of the ticket data in bytes.
* @param [out] encLen Output length of the ticket data.
* @param [in] ctx Ignored. Application specific data.
* @return WOLFSSL_TICKET_RET_OK on success.
* @return WOLFSSL_TICKET_RET_CREATE when a new ticket is required.
* @return WOLFSSL_TICKET_RET_FATAL on error.
*/
static int wolfSSL_TicketKeyCb(WOLFSSL* ssl,
unsigned char keyName[WOLFSSL_TICKET_NAME_SZ],
unsigned char iv[WOLFSSL_TICKET_IV_SZ],
unsigned char mac[WOLFSSL_TICKET_MAC_SZ],
int enc, unsigned char* encTicket,
int encTicketLen, int* encLen, void* ctx)
{
WC_DECLARE_VAR(evpCtx, WOLFSSL_EVP_CIPHER_CTX, 1, 0);
int ret = WOLFSSL_TICKET_RET_OK;
(void)ctx;
WOLFSSL_ENTER("wolfSSL_TicketKeyCb");
if ((ssl == NULL) || (ssl->ctx == NULL) ||
(ssl->ctx->ticketEncWrapCb == NULL)) {
WOLFSSL_MSG("Bad parameter");
ret = WOLFSSL_TICKET_RET_FATAL;
}
#ifdef WOLFSSL_SMALL_STACK
if (ret == WOLFSSL_TICKET_RET_OK) {
evpCtx = (WOLFSSL_EVP_CIPHER_CTX *)XMALLOC(sizeof(*evpCtx), ssl->heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (evpCtx == NULL) {
WOLFSSL_MSG("out of memory");
ret = WOLFSSL_TICKET_RET_FATAL;
}
}
#endif
if (ret == WOLFSSL_TICKET_RET_OK) {
WOLFSSL_HMAC_CTX hmacCtx;
/* Initialize the cipher and HMAC. */
wolfSSL_EVP_CIPHER_CTX_init(evpCtx);
if (wolfSSL_HMAC_CTX_Init(&hmacCtx) != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("wolfSSL_HMAC_CTX_Init error");
ret = WOLFSSL_TICKET_RET_FATAL;
}
if (ret == WOLFSSL_TICKET_RET_OK) {
int res;
int totalSz = 0;
res = ssl->ctx->ticketEncWrapCb(ssl, keyName, iv, evpCtx, &hmacCtx,
enc);
if ((res != TICKET_KEY_CB_RET_OK) &&
(res != TICKET_KEY_CB_RET_RENEW)) {
WOLFSSL_MSG("Ticket callback error");
ret = WOLFSSL_TICKET_RET_FATAL;
}
if (ret == WOLFSSL_TICKET_RET_OK) {
if (wolfSSL_HMAC_size(&hmacCtx) > WOLFSSL_TICKET_MAC_SZ) {
WOLFSSL_MSG("Ticket cipher MAC size error");
ret = WOLFSSL_TICKET_RET_FATAL;
}
}
if (ret == WOLFSSL_TICKET_RET_OK) {
if (enc) {
if (!wolfssl_ticket_key_enc(evpCtx, &hmacCtx, encTicket,
encTicketLen, *encLen, mac, &totalSz)) {
ret = WOLFSSL_TICKET_RET_FATAL;
}
}
else {
if (!wolfssl_ticket_key_dec(evpCtx, &hmacCtx, encTicket,
encTicketLen, mac, &totalSz)) {
ret = WOLFSSL_TICKET_RET_FATAL;
}
}
}
if (ret == WOLFSSL_TICKET_RET_OK) {
*encLen = totalSz;
if ((res == TICKET_KEY_CB_RET_RENEW) &&
(!IsAtLeastTLSv1_3(ssl->version)) && (!enc)) {
ret = WOLFSSL_TICKET_RET_CREATE;
}
else {
ret = WOLFSSL_TICKET_RET_OK;
}
}
(void)wc_HmacFree(&hmacCtx.hmac);
}
(void)wolfSSL_EVP_CIPHER_CTX_cleanup(evpCtx);
WC_FREE_VAR_EX(evpCtx, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
return ret;
}
/* Set the OpenSSL-style session ticket key callback on the context.
*
* Installs a wrapper as the ticket encryption callback.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] cb OpenSSL session ticket key callback.
* @return WOLFSSL_SUCCESS on success.
*/
int wolfSSL_CTX_set_tlsext_ticket_key_cb(WOLFSSL_CTX *ctx, ticketCompatCb cb)
{
/* Set the ticket encryption callback to be a wrapper around OpenSSL
* callback.
*/
ctx->ticketEncCb = wolfSSL_TicketKeyCb;
ctx->ticketEncWrapCb = cb;
return WOLFSSL_SUCCESS;
}
#endif /* HAVE_SESSION_TICKET */
#endif /* OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY ||
OPENSSL_EXTRA || HAVE_LIGHTY */
#if defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
!defined(NO_WOLFSSL_SERVER)
/* Serialize the session ticket encryption keys.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] keys Buffer to hold session ticket keys.
* @param [in] keylen Length of buffer.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ctx is NULL, keys is NULL or keylen is not the
* correct length.
*/
long wolfSSL_CTX_get_tlsext_ticket_keys(WOLFSSL_CTX *ctx,
unsigned char *keys, int keylen)
{
if (ctx == NULL || keys == NULL) {
return WOLFSSL_FAILURE;
}
if (keylen != WOLFSSL_TICKET_KEYS_SZ) {
return WOLFSSL_FAILURE;
}
XMEMCPY(keys, ctx->ticketKeyCtx.name, WOLFSSL_TICKET_NAME_SZ);
keys += WOLFSSL_TICKET_NAME_SZ;
XMEMCPY(keys, ctx->ticketKeyCtx.key[0], WOLFSSL_TICKET_KEY_SZ);
keys += WOLFSSL_TICKET_KEY_SZ;
XMEMCPY(keys, ctx->ticketKeyCtx.key[1], WOLFSSL_TICKET_KEY_SZ);
keys += WOLFSSL_TICKET_KEY_SZ;
c32toa(ctx->ticketKeyCtx.expirary[0], keys);
keys += OPAQUE32_LEN;
c32toa(ctx->ticketKeyCtx.expirary[1], keys);
return WOLFSSL_SUCCESS;
}
/* Deserialize the session ticket encryption keys.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] keys Session ticket keys.
* @param [in] keylen Length of data.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ctx is NULL, keys is NULL or keylen is not the
* correct length.
*/
long wolfSSL_CTX_set_tlsext_ticket_keys(WOLFSSL_CTX *ctx,
const void *keys_vp, int keylen)
{
const byte* keys = (const byte*)keys_vp;
if (ctx == NULL || keys == NULL) {
return WOLFSSL_FAILURE;
}
if (keylen != WOLFSSL_TICKET_KEYS_SZ) {
return WOLFSSL_FAILURE;
}
XMEMCPY(ctx->ticketKeyCtx.name, keys, WOLFSSL_TICKET_NAME_SZ);
keys += WOLFSSL_TICKET_NAME_SZ;
XMEMCPY(ctx->ticketKeyCtx.key[0], keys, WOLFSSL_TICKET_KEY_SZ);
keys += WOLFSSL_TICKET_KEY_SZ;
XMEMCPY(ctx->ticketKeyCtx.key[1], keys, WOLFSSL_TICKET_KEY_SZ);
keys += WOLFSSL_TICKET_KEY_SZ;
ato32(keys, &ctx->ticketKeyCtx.expirary[0]);
keys += OPAQUE32_LEN;
ato32(keys, &ctx->ticketKeyCtx.expirary[1]);
return WOLFSSL_SUCCESS;
}
#endif
#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \
defined(WOLFSSL_HAPROXY) || defined(HAVE_LIGHTY) || \
defined(WOLFSSL_QUIC)
#ifdef HAVE_ALPN
/* Get the ALPN protocol selected for the object.
*
* @param [in] ssl SSL/TLS object.
* @param [out] data Selected protocol data.
* @param [out] len Length of the protocol data in bytes.
*/
void wolfSSL_get0_alpn_selected(const WOLFSSL *ssl, const unsigned char **data,
unsigned int *len)
{
word16 nameLen = 0;
if ((ssl != NULL) && (data != NULL) && (len != NULL)) {
TLSX_ALPN_GetRequest(ssl->extensions, (void **)data, &nameLen);
*len = nameLen;
}
}
/* Determine whether a protocol appears in the client's protocol list.
*
* The client's list is in wire format: each entry is a length byte followed
* by that many protocol-name bytes.
*
* @param [in] proto Protocol name to look for.
* @param [in] protoLen Length of the protocol name in bytes.
* @param [in] clientNames Client's protocol list.
* @param [in] clientLen Length of the client's list in bytes.
* @return 1 when the protocol is in the list.
* @return 0 when the protocol is not in the list.
*/
static int wolfssl_protocol_in_list(const unsigned char* proto, byte protoLen,
const unsigned char* clientNames, unsigned int clientLen)
{
unsigned int j;
byte lenClient;
int found = 0;
/* Compare against each of the client's length-prefixed names. */
for (j = 0; j < clientLen; j += lenClient) {
lenClient = clientNames[j++];
if ((lenClient == 0) || (j + lenClient > clientLen)) {
break;
}
if ((protoLen == lenClient) &&
(XMEMCMP(proto, clientNames + j, protoLen) == 0)) {
found = 1;
break;
}
}
return found;
}
/* Select the next protocol from the peer's list that matches the client's.
*
* On no overlap, the first client protocol is selected.
*
* @param [out] out Selected protocol data.
* @param [out] outLen Length of the selected protocol in bytes.
* @param [in] in Peer's protocol list.
* @param [in] inLen Length of the peer's list in bytes.
* @param [in] clientNames Client's protocol list.
* @param [in] clientLen Length of the client's list in bytes.
* @return WOLFSSL_NPN_NEGOTIATED when a match was found.
* @return WOLFSSL_NPN_NO_OVERLAP when no match was found.
* @return WOLFSSL_NPN_UNSUPPORTED when an argument is NULL.
*/
int wolfSSL_select_next_proto(unsigned char **out, unsigned char *outLen,
const unsigned char *in, unsigned int inLen,
const unsigned char *clientNames, unsigned int clientLen)
{
unsigned int i;
byte lenIn;
int ret = WOLFSSL_NPN_NO_OVERLAP;
if ((out == NULL) || (outLen == NULL) || (in == NULL) ||
(clientNames == NULL)) {
ret = WOLFSSL_NPN_UNSUPPORTED;
}
else {
/* Walk the peer's list; each entry is a length byte then that many
* protocol-name bytes. */
for (i = 0; i < inLen; i += lenIn) {
lenIn = in[i++];
/* Stop on an empty entry or one that runs past the buffer. */
if ((lenIn == 0) || (i + lenIn > inLen)) {
break;
}
/* Select this peer protocol if the client also offered it. */
if (wolfssl_protocol_in_list(in + i, lenIn, clientNames,
clientLen)) {
*out = (unsigned char *)(in + i);
*outLen = lenIn;
ret = WOLFSSL_NPN_NEGOTIATED;
break;
}
}
if (ret != WOLFSSL_NPN_NEGOTIATED) {
/* No overlap: fall back to the client's first protocol. */
if ((clientLen > 0) &&
((unsigned int)clientNames[0] + 1 <= clientLen)) {
*out = (unsigned char *)clientNames + 1;
*outLen = clientNames[0];
}
else {
*out = (unsigned char *)clientNames;
*outLen = 0;
}
ret = WOLFSSL_NPN_NO_OVERLAP;
}
}
return ret;
}
/* Set the ALPN selection callback on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] cb ALPN selection callback.
* @param [in] arg User argument passed to the callback.
*/
void wolfSSL_set_alpn_select_cb(WOLFSSL *ssl,
int (*cb)(WOLFSSL *ssl, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg),
void *arg)
{
if (ssl != NULL) {
ssl->alpnSelect = cb;
ssl->alpnSelectArg = arg;
}
}
/* Set the ALPN selection callback on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] cb ALPN selection callback.
* @param [in] arg User argument passed to the callback.
*/
void wolfSSL_CTX_set_alpn_select_cb(WOLFSSL_CTX *ctx,
int (*cb)(WOLFSSL *ssl, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg),
void *arg)
{
if (ctx != NULL) {
ctx->alpnSelect = cb;
ctx->alpnSelectArg = arg;
}
}
/* Set the NPN advertised-protocols callback on the context.
*
* Not implemented - stub for OpenSSL compatibility.
*
* @param [in] s SSL/TLS context object.
* @param [in] cb NPN advertised-protocols callback.
* @param [in] arg User argument passed to the callback.
*/
void wolfSSL_CTX_set_next_protos_advertised_cb(WOLFSSL_CTX *s,
int (*cb)(WOLFSSL *ssl, const unsigned char **out, unsigned int *outlen,
void *arg), void *arg)
{
(void)s;
(void)cb;
(void)arg;
WOLFSSL_STUB("wolfSSL_CTX_set_next_protos_advertised_cb");
}
/* Set the NPN protocol-selection callback on the context.
*
* Not implemented - stub for OpenSSL compatibility.
*
* @param [in] s SSL/TLS context object.
* @param [in] cb NPN protocol-selection callback.
* @param [in] arg User argument passed to the callback.
*/
void wolfSSL_CTX_set_next_proto_select_cb(WOLFSSL_CTX *s,
int (*cb)(WOLFSSL *ssl, unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg),
void *arg)
{
(void)s;
(void)cb;
(void)arg;
WOLFSSL_STUB("wolfSSL_CTX_set_next_proto_select_cb");
}
/* Get the NPN protocol negotiated for the object.
*
* Not implemented - stub for OpenSSL compatibility.
*
* @param [in] s SSL/TLS object.
* @param [out] data Negotiated protocol data.
* @param [out] len Length of the protocol data in bytes.
*/
void wolfSSL_get0_next_proto_negotiated(const WOLFSSL *s,
const unsigned char **data, unsigned *len)
{
(void)s;
(void)data;
(void)len;
WOLFSSL_STUB("wolfSSL_get0_next_proto_negotiated");
}
#endif /* HAVE_ALPN */
#endif /* WOLFSSL_NGINX / WOLFSSL_HAPROXY */
#if defined(OPENSSL_EXTRA) || defined(HAVE_CURL)
/* Determine whether an elliptic curve is disabled for the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] curve_id Curve identifier.
* @return 1 when the curve is disabled or out of range.
* @return 0 when the curve is enabled or is an FFDHE group.
*/
int wolfSSL_curve_is_disabled(const WOLFSSL* ssl, word16 curve_id)
{
int ret = 0;
WOLFSSL_ENTER("wolfSSL_curve_is_disabled");
WOLFSSL_MSG_EX("wolfSSL_curve_is_disabled checking for %d", curve_id);
/* (curve_id >= WOLFSSL_FFDHE_START) - DH parameters are never disabled. */
if (curve_id < WOLFSSL_FFDHE_START) {
if (curve_id > WOLFSSL_ECC_MAX_AVAIL) {
WOLFSSL_MSG("Curve id out of supported range");
/* Disabled if not in valid range. */
ret = 1;
}
else if (curve_id >= 32) {
/* 0 is for invalid and 1-14 aren't used otherwise. */
ret = (ssl->disabledCurves & (1U << (curve_id - 32))) != 0;
}
else {
ret = (ssl->disabledCurves & (1U << curve_id)) != 0;
}
}
WOLFSSL_LEAVE("wolfSSL_curve_is_disabled", ret);
return ret;
}
#if (defined(HAVE_ECC) || \
defined(HAVE_CURVE25519) || defined(HAVE_CURVE448))
/* Set the supported curves list, by name, on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] names Colon-separated list of curve names.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ctx or names is NULL or on error.
*/
int wolfSSL_CTX_set1_curves_list(WOLFSSL_CTX* ctx, const char* names)
{
WOLFSSL_ENTER("wolfSSL_CTX_set1_curves_list");
if (ctx == NULL || names == NULL) {
WOLFSSL_MSG("ctx or names was NULL");
return WOLFSSL_FAILURE;
}
return set_curves_list(NULL, ctx, names, 1);
}
/* Set the supported curves list, by name, on the object.
*
* @param [in] ssl SSL/TLS object.
* @param [in] names Colon-separated list of curve names.
* @return WOLFSSL_SUCCESS on success.
* @return WOLFSSL_FAILURE when ssl or names is NULL or on error.
*/
int wolfSSL_set1_curves_list(WOLFSSL* ssl, const char* names)
{
WOLFSSL_ENTER("wolfSSL_set1_curves_list");
if (ssl == NULL || names == NULL) {
WOLFSSL_MSG("ssl or names was NULL");
return WOLFSSL_FAILURE;
}
return set_curves_list(ssl, NULL, names, 1);
}
#endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
#endif /* OPENSSL_EXTRA || HAVE_CURL */
#ifdef OPENSSL_EXTRA
/* Set the ALPN protocol list, in wire format, on the context.
*
* @param [in] ctx SSL/TLS context object.
* @param [in] p ALPN protocol list in wire format (length-prefixed).
* @param [in] p_len Length of the protocol list in bytes.
* @return WOLFSSL_SUCCESS (or 0 with WOLFSSL_ERROR_CODE_OPENSSL) on success.
* @return BAD_FUNC_ARG when ctx or p is NULL.
* @return WOLFSSL_FAILURE (or 1 with WOLFSSL_ERROR_CODE_OPENSSL) on error.
*/
int wolfSSL_CTX_set_alpn_protos(WOLFSSL_CTX *ctx, const unsigned char *p,
unsigned int p_len)
{
WOLFSSL_ENTER("wolfSSL_CTX_set_alpn_protos");
if (ctx == NULL || p == NULL)
return BAD_FUNC_ARG;
if (ctx->alpn_cli_protos != NULL) {
XFREE((void*)ctx->alpn_cli_protos, ctx->heap, DYNAMIC_TYPE_OPENSSL);
}
ctx->alpn_cli_protos = (const unsigned char*)XMALLOC(p_len,
ctx->heap, DYNAMIC_TYPE_OPENSSL);
if (ctx->alpn_cli_protos == NULL) {
#if defined(WOLFSSL_ERROR_CODE_OPENSSL)
/* 0 on success in OpenSSL, non-0 on failure in OpenSSL
* the function reverses the return value convention.
*/
return 1;
#else
return WOLFSSL_FAILURE;
#endif
}
XMEMCPY((void*)ctx->alpn_cli_protos, p, p_len);
ctx->alpn_cli_protos_len = p_len;
#if defined(WOLFSSL_ERROR_CODE_OPENSSL)
/* 0 on success in OpenSSL, non-0 on failure in OpenSSL
* the function reverses the return value convention.
*/
return 0;
#else
return WOLFSSL_SUCCESS;
#endif
}
#ifdef HAVE_ALPN
#ifndef NO_BIO
/* Convert a wire-format ALPN protocol list into a comma-separated string.
*
* The wire format is a sequence of entries, each a length byte followed by
* that many protocol-name bytes.
*
* @param [in] p ALPN protocol list in wire format.
* @param [in] p_len Length of the protocol list in bytes.
* @param [out] pt Buffer to hold the comma-separated list. Must hold at
* least p_len bytes.
* @param [out] ptLen Length of the comma-separated list written.
* @return 1 on success.
* @return 0 when the wire format is invalid.
*/
static int wolfssl_alpn_protos_to_list(const unsigned char* p,
unsigned int p_len, char* pt, unsigned int* ptLen)
{
unsigned int idx = 0;
unsigned int ptIdx = 0;
unsigned int sz;
int ret = 1;
/* Convert into a comma separated list. */
while (idx < p_len - 1) {
unsigned int i;
sz = p[idx++];
if (idx + sz > p_len) {
WOLFSSL_MSG("Bad list format");
ret = 0;
break;
}
if (sz > 0) {
for (i = 0; i < sz; i++) {
pt[ptIdx++] = p[idx++];
}
if (idx < p_len - 1) {
pt[ptIdx++] = ',';
}
}
}
if (ret == 1) {
*ptLen = ptIdx;
}
return ret;
}
/* Set the ALPN protocol list, in wire format, on the object.
*
* The list is length-prefixed, e.g.
* unsigned char p[] = { 8, 'h','t','t','p','/','1','.','1' };
*
* @param [in] ssl SSL/TLS object.
* @param [in] p ALPN protocol list in wire format (length-prefixed).
* @param [in] p_len Length of the protocol list in bytes.
* @return WOLFSSL_SUCCESS (or 0 with WOLFSSL_ERROR_CODE_OPENSSL) on success.
* @return WOLFSSL_FAILURE (or 1 with WOLFSSL_ERROR_CODE_OPENSSL) on error.
*/
int wolfSSL_set_alpn_protos(WOLFSSL* ssl,
const unsigned char* p, unsigned int p_len)
{
char* pt = NULL;
unsigned int ptIdx = 0;
/* RFC 7301: a server that does not select any of the client's offered
* protocols MUST send no_application_protocol. Match that contract on
* the OpenSSL-compat surface rather than silently continuing. */
int alpn_opt = WOLFSSL_ALPN_FAILED_ON_MISMATCH;
#if defined(WOLFSSL_ERROR_CODE_OPENSSL)
int ret = 1;
#else
int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
#endif
WOLFSSL_ENTER("wolfSSL_set_alpn_protos");
if ((ssl != NULL) && (p_len > 1) && (p != NULL)) {
/* Replacing leading number with trailing ',' and adding '\0'. */
pt = (char*)XMALLOC(p_len + 1, ssl->heap, DYNAMIC_TYPE_OPENSSL);
if (pt != NULL) {
if (wolfssl_alpn_protos_to_list(p, p_len, pt, &ptIdx)) {
pt[ptIdx++] = '\0';
/* Clear out all currently set ALPN extensions. */
TLSX_Remove(&ssl->extensions, TLSX_APPLICATION_LAYER_PROTOCOL,
ssl->heap);
if (wolfSSL_UseALPN(ssl, pt, ptIdx, (byte)alpn_opt) ==
WOLFSSL_SUCCESS) {
#if defined(WOLFSSL_ERROR_CODE_OPENSSL)
ret = 0;
#else
ret = WOLFSSL_SUCCESS;
#endif
}
}
XFREE(pt, ssl->heap, DYNAMIC_TYPE_OPENSSL);
}
}
return ret;
}
#endif /* !NO_BIO */
#endif /* HAVE_ALPN */
#endif /* OPENSSL_EXTRA */
#endif /* !WOLFCRYPT_ONLY */
#endif /* !WOLFSSL_SSL_API_EXT_INCLUDED */